# -*- coding: utf-8 -*-
"""
================================================================================
 GENERATOR RPM (RANCANGAN PEMBELAJARAN MENDALAM) - KURIKULUM NASIONAL 2025
================================================================================

 Menghasilkan dokumen RPM (.docx) sesuai FORMAT TEMPLAT RESMI Pembelajaran
 Mendalam (Hal. 38-39), yaitu:

   TABEL IDENTITAS        (Nama Instansi, NPSN, Penyusun, Mata Pelajaran,
                           Fase, Kelas, Alokasi Waktu, Materi Pokok)
   A. IDENTIFIKASI        (Murid, Materi Pelajaran, Dimensi Profil Lulusan)
   B. DESAIN PEMBELAJARAN (CP, Lintas Disiplin Ilmu, Tujuan Pembelajaran,
                           Topik, Praktik Pedagogis, Kemitraan, Lingkungan,
                           Pemanfaatan Digital)
   C. PENGALAMAN BELAJAR  (Awal - Inti [Memahami/Mengaplikasi/Merefleksi] -
                           Penutup)
   D. ASESMEN PEMBELAJARAN(Awal, Proses, Akhir)
   LAMPIRAN               (instrumen + form penilaian 10 nama murid)

 SUMBER ISI (tidak ada karangan bebas):
   * Elemen & Capaian Pembelajaran  -> cp_db.json (Kepmen BSKAP 046/H/KR/2025)
   * Alokasi waktu / pertemuan      -> mesin ATP (atp.susun_rencana) sehingga
                                       ATP dan RPM selalu SINKRON
   * Kerangka Pembelajaran Mendalam -> buku "Pembelajaran Mendalam" (prinsip
                                       berkesadaran-bermakna-menggembirakan,
                                       pengalaman memahami-mengaplikasi-
                                       merefleksi, 8 Dimensi Profil Lulusan)

 PILIHAN LINGKUP:
   --lingkup tahun     -> semua elemen kelas tsb (2 semester), 1 DOCX per elemen
   --lingkup semester  -> 1 semester saja, 1 DOCX per elemen
   --lingkup elemen    -> 1 Elemen/CP = 1 DOCX
   --lingkup tp        -> kelompok Tujuan Pembelajaran pilihan = 1 DOCX
                         (pilih Elemen/CP dengan --elemen N, lalu --tp "1,2,4")
  --lingkup topik     -> 1 Materi Pokok = 1 DOCX; satu CP dipecah menjadi
                         beberapa Materi Pokok, tiap berkas memuat beberapa
                         pertemuan sesuai alokasi waktu ATP
                          (--tp "1,2,4" --tp-per-pertemuan 1)

   Pada semua lingkup, bagian C PENGALAMAN BELAJAR diuraikan untuk SETIAP
   pertemuan sesuai alokasi waktu pada ATP.

 Contoh:
   python rpm.py --form
   python rpm.py --mapel "dasar-dasar teknik otomotif" --kelas X --jp 11 --jpp 11 \
                 --lingkup elemen --elemen 1
================================================================================
"""

import argparse
import io
import math
import os
import re
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

import atp
from atp import (KEPALA_NAMA, KEPALA_NPA, TITIK, blok_ttd, npa_teks, tanggal_ttd,
                 BASE_OUT, FONT, DPL, KELAS_FASE, STRUKTUR, bagi_minggu,
                 cari_mapel, kapital, kecil_awal, load_db, pecah_klausa,
                 semester_aktif, shade, style_run, susun_rencana)

from docx import Document
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT

J = WD_ALIGN_PARAGRAPH.JUSTIFY
C = WD_ALIGN_PARAGRAPH.CENTER
L = WD_ALIGN_PARAGRAPH.LEFT

RUJUKAN_CP = ("Keputusan Kepala Badan Standar, Kurikulum, dan Asesmen Pendidikan "
              "Nomor 046/H/KR/2025 tentang Capaian Pembelajaran")

# 10 nama murid CONTOH (silakan ganti dengan daftar kelas yang sebenarnya)
MURID_CONTOH = [
    "Adinda Puspa Maharani",
    "Bagas Arya Pratama",
    "Cahya Nur Ramadhan",
    "Dewi Anggraini Lestari",
    "Fajar Setiawan",
    "Gita Ramadhani",
    "Hafiz Alfarizi",
    "Intan Permata Sari",
    "Joko Prasetyo",
    "Kirana Ayu Wulandari",
]

# ============================================================================
# 1. KARAKTER MATA PELAJARAN
# ============================================================================


def karakter_mapel(s, nama_elemen, cp):
    """Menentukan watak mapel supaya asesmen/lingkungan/metode relevan."""
    blob = (s["nama_asli"] + " " + nama_elemen + " " + cp).lower()
    if any(k in blob for k in ["tari", "musik", "teater", "seni", "karawitan",
                               "pedalangan", "rupa", "desain komunikasi"]):
        return "seni"
    if any(k in blob for k in ["jasmani", "olahraga", "kebugaran", "pjok"]):
        return "olahraga"
    if s["lampiran"] == "III":
        return "kejuruan"
    if any(k in blob for k in ["bahasa", "teks", "menyimak", "membaca", "menulis",
                               "berbicara", "literasi"]):
        return "bahasa"
    if any(k in blob for k in ["informatika", "koding", "algoritma", "pemrograman",
                               "kecerdasan artifisial"]):
        return "informatika"
    return "umum"


PRAKTIS = ("kejuruan", "seni", "olahraga", "informatika")

# Mata pelajaran kejuruan (Lampiran III) wajib memakai Pembelajaran Berbasis
# Proyek (PJBL). Mata pelajaran umum bebas memakai model lain yang relevan
# dengan Capaian Pembelajarannya.
WATAK_PJBL = ("kejuruan",)

ALASAN_PJBL_KEJURUAN = (
    "mata pelajaran kejuruan menuntut Murid bekerja pada penugasan nyata "
    "seperti yang berlaku di dunia kerja, sehingga pembelajaran dirancang "
    "dengan Pembelajaran Berbasis Proyek (Project Based Learning): Murid "
    "mengerjakan proyek/pekerjaan yang menghasilkan produk atau unjuk kerja "
    "yang dapat dinilai, sesuai prosedur kerja (SOP) dan ketentuan K3"
)

RUANG_FISIK = {
    "kejuruan": "ruang praktik/bengkel dan laboratorium program keahlian, area kerja "
                "dengan penataan alat sesuai standar 5S, serta ruang penyimpanan alat dan bahan",
    "seni": "ruang praktik seni/sanggar, aula atau panggung sekolah, dan ruang "
            "penyimpanan properti serta instrumen",
    "olahraga": "lapangan, aula, dan ruang terbuka sekolah yang aman untuk aktivitas gerak",
    "informatika": "laboratorium komputer dengan jaringan internet dan ruang kelas kolaboratif",
    "bahasa": "ruang kelas dengan penataan kursi berkelompok, perpustakaan, dan pojok baca",
    "umum": "ruang kelas dengan penataan kursi berkelompok, perpustakaan, dan lingkungan sekitar sekolah",
}

KEMITRAAN = {
    "kejuruan": [
        "**Lingkungan sekolah:** kepala sekolah dan pengawas (supervisi akademik), "
        "ketua program keahlian, kepala bengkel/toolman, serta guru mata pelajaran "
        "lain untuk penguatan lintas disiplin ilmu.",
        "**Luar sekolah:** Dunia Usaha/Dunia Industri/Dunia Kerja (DUDIKA) mitra "
        "program keahlian sebagai narasumber praktisi (guru tamu), tempat observasi "
        "lapangan, dan sumber standar kerja yang berlaku di industri; MGMP "
        "kejuruan sebagai forum penyelarasan perangkat ajar.",
        "**Masyarakat:** orang tua/wali sebagai pendukung disiplin dan kesiapan "
        "belajar Murid, serta bengkel/usaha di lingkungan sekitar sekolah sebagai "
        "konteks belajar nyata.",
    ],
    "seni": [
        "**Lingkungan sekolah:** kepala sekolah dan pengawas (supervisi akademik), "
        "guru seni serumpun, serta pengelola aula/panggung sekolah.",
        "**Luar sekolah:** sanggar dan komunitas seni setempat, seniman/pelaku seni "
        "sebagai narasumber, dewan kesenian daerah, serta MGMP Seni Budaya.",
        "**Masyarakat:** orang tua/wali, tokoh budaya, dan komunitas adat sebagai "
        "sumber pengetahuan tradisi.",
    ],
    "umum": [
        "**Lingkungan sekolah:** kepala sekolah dan pengawas (supervisi akademik), "
        "guru mata pelajaran lain untuk penguatan lintas disiplin ilmu, serta "
        "pengelola perpustakaan.",
        "**Luar sekolah:** MGMP mata pelajaran sebagai forum penyelarasan perangkat "
        "ajar, narasumber/praktisi yang relevan dengan topik, serta sumber belajar digital tepercaya.",
        "**Masyarakat:** orang tua/wali dan komunitas belajar di lingkungan sekitar sekolah.",
    ],
}
KEMITRAAN["olahraga"] = KEMITRAAN["umum"]
KEMITRAAN["bahasa"] = KEMITRAAN["umum"]
KEMITRAAN["informatika"] = KEMITRAAN["umum"]

STIMULUS = {
    "kejuruan": "tayangan video kerja nyata di bengkel/industri dan penyajian "
                "benda/komponen asli sebagai benda nyata",
    "seni": "tayangan karya atau rekaman pertunjukan serta peragaan singkat oleh guru",
    "olahraga": "peragaan gerak dan tayangan video teknik yang benar",
    "informatika": "demonstrasi singkat program/aplikasi dan kasus penggunaan nyata",
    "bahasa": "teks/tayangan autentik yang dekat dengan pengalaman Murid",
    "umum": "tayangan video, gambar, atau kasus nyata yang dekat dengan pengalaman Murid",
}

SUMBER = {
    "kejuruan": "buku teks, lembar informasi/service manual, video kerja, jobsheet, "
                "dan pengamatan langsung pada unit/trainer di bengkel",
    "seni": "buku teks, rekaman karya/pertunjukan, narasumber seniman, dan pengamatan langsung",
    "olahraga": "buku teks, video teknik gerak, dan pengamatan langsung saat praktik",
    "informatika": "buku teks, dokumentasi resmi, video tutorial, dan eksperimen langsung di komputer",
    "bahasa": "buku teks, teks autentik cetak dan digital, serta media massa tepercaya",
    "umum": "buku teks, modul ajar, sumber digital tepercaya, dan lingkungan sekitar",
}

# ============================================================================
# 2. LINTAS DISIPLIN ILMU
# ============================================================================

LINTAS_RUMPUN = [
    (["otomotif", "kendaraan", "sepeda motor", "engine", "ototronik", "alat berat",
      "body", "mesin", "pemesinan", "pengelasan", "manufaktur", "logam"],
     [("Fisika", "konsep gaya, momen/torsi, tekanan, kalor, dan kelistrikan pada sistem teknik"),
      ("Matematika", "pengukuran, konversi satuan, perbandingan, dan perhitungan hasil kerja"),
      ("Kimia", "karakteristik bahan bakar, pelumas, cairan pendingin, korosi, dan sifat bahan"),
      ("Informatika", "pemanfaatan perangkat/aplikasi digital untuk penelusuran data teknis dan pelaporan"),
      ("Bahasa Indonesia", "penyusunan laporan kerja dan komunikasi teknis yang santun")]),
    (["tari", "musik", "teater", "seni", "karawitan", "pedalangan", "rupa"],
     [("Sejarah", "latar sejarah dan perkembangan bentuk seni yang dipelajari"),
      ("Sosiologi/Antropologi budaya", "fungsi seni dalam masyarakat serta nilai tradisi"),
      ("Bahasa Indonesia", "penulisan deskripsi karya, sinopsis, dan apresiasi tertulis"),
      ("Informatika", "dokumentasi, penyuntingan audio/video, dan publikasi karya"),
      ("PJOK", "kelenturan, kebugaran, dan olah tubuh sebagai penunjang praktik")]),
    (["boga", "kuliner", "pertanian", "perikanan", "peternakan", "agribisnis", "pangan"],
     [("Biologi", "pertumbuhan organisme, mikrobiologi, dan sanitasi bahan"),
      ("Kimia", "reaksi bahan, pengawetan, dan keamanan pangan"),
      ("Matematika", "perhitungan resep, dosis, rendemen, dan biaya"),
      ("Kewirausahaan", "peluang usaha, pengemasan, dan pemasaran hasil"),
      ("Bahasa Indonesia", "penyusunan laporan dan promosi produk")]),
    (["akuntansi", "bisnis", "pemasaran", "manajemen", "perkantoran", "logistik"],
     [("Matematika", "operasi hitung, persentase, dan penyajian data keuangan"),
      ("Ekonomi", "konsep pasar, harga, dan perilaku konsumen"),
      ("Informatika", "pengolahan data dengan lembar kerja dan aplikasi bisnis"),
      ("Bahasa Indonesia", "korespondensi, laporan, dan komunikasi bisnis"),
      ("Pendidikan Pancasila", "etika profesi, kejujuran, dan kepatuhan aturan")]),
    (["komputer", "jaringan", "informatika", "perangkat lunak", "rekayasa perangkat",
      "telekomunikasi", "animasi", "multimedia", "koding"],
     [("Matematika", "logika, bilangan, dan pemodelan"),
      ("Fisika", "kelistrikan, gelombang, dan transmisi sinyal"),
      ("Bahasa Inggris", "pembacaan dokumentasi dan istilah teknis"),
      ("Bahasa Indonesia", "penyusunan dokumentasi dan presentasi hasil"),
      ("Seni Budaya", "prinsip desain visual dan estetika antarmuka")]),
    (["kesehatan", "keperawatan", "farmasi", "kecantikan", "spa"],
     [("Biologi", "anatomi, fisiologi, dan mikrobiologi"),
      ("Kimia", "sifat bahan, sediaan, dan reaksi zat"),
      ("Matematika", "perhitungan dosis dan pencatatan data"),
      ("Pendidikan Pancasila", "etika layanan dan kerahasiaan klien"),
      ("Bahasa Indonesia", "komunikasi terapeutik dan pelaporan")]),
    (["bangunan", "konstruksi", "sipil", "arsitektur", "geomatika", "furnitur", "kayu"],
     [("Matematika", "geometri, skala, luas, volume, dan trigonometri"),
      ("Fisika", "gaya, beban, kesetimbangan, dan sifat bahan"),
      ("Kimia", "sifat material, campuran, dan keawetan bahan"),
      ("Informatika", "penggambaran dan pemodelan digital"),
      ("Bahasa Indonesia", "penyusunan laporan dan spesifikasi pekerjaan")]),
    (["listrik", "elektronika", "instalasi", "mekatronika", "energi", "pendingin"],
     [("Fisika", "hukum Ohm, rangkaian listrik, elektromagnetik, dan energi"),
      ("Matematika", "operasi hitung, perbandingan, dan pembacaan grafik"),
      ("Informatika", "simulasi rangkaian dan pemrograman kendali"),
      ("Bahasa Indonesia", "penyusunan laporan kerja dan pembacaan manual"),
      ("PJOK", "kesehatan dan keselamatan kerja serta ergonomi")]),
]

LINTAS_ELEMEN = [
    (["gambar teknik", "gambar", "proyeksi", "sketsa"],
     [("Matematika", "geometri, proyeksi, skala, dan konstruksi geometris")]),
    (["k3", "k3lh", "keselamatan", "budaya kerja", "kecakapan kerja"],
     [("PJOK", "kesehatan, kebugaran, dan ergonomi kerja"),
      ("Pendidikan Pancasila", "tanggung jawab, kedisiplinan, dan kepatuhan terhadap aturan kerja")]),
    (["kelistrikan", "elektronika", "baterai", "sensor"],
     [("Fisika", "rangkaian listrik, hukum Ohm, dan elektromagnetik")]),
    (["hidrolik", "pneumatik", "fluida"],
     [("Fisika", "hukum Pascal, tekanan, dan mekanika fluida")]),
    (["wawasan", "peluang", "bisnis", "wirausaha", "industri", "profesi", "karier"],
     [("Ekonomi/Kewirausahaan", "peluang usaha, rantai nilai, dan layanan purnajual"),
      ("Bimbingan Karier", "pengenalan profesi dan perencanaan karier Murid")]),
    (["lingkungan", "limbah", "emisi", "energi terbarukan", "listrik dan hybrid"],
     [("Biologi", "dampak kegiatan manusia terhadap lingkungan dan pengelolaan limbah")]),
    (["pengukuran", "alat ukur", "metrologi"],
     [("Matematika", "ketelitian, pembulatan, dan konversi satuan")]),
]

LINTAS_DEFAULT = [
    ("Bahasa Indonesia", "penyusunan laporan dan penyajian hasil belajar secara runtut"),
    ("Matematika", "pengolahan data sederhana dan penalaran kuantitatif"),
    ("Informatika", "pemanfaatan perangkat digital untuk mencari, mengolah, dan menyajikan informasi"),
    ("Pendidikan Pancasila", "penguatan tanggung jawab, kerja sama, dan etika"),
]


def lintas_disiplin(nama_mapel, elemen, cp, maksimal=5):
    blob = (nama_mapel + " " + elemen + " " + cp).lower()
    hasil = []

    def tambah(daftar):
        for nama, ket in daftar:
            if nama not in [h[0] for h in hasil]:
                hasil.append((nama, ket))

    for keys, daftar in LINTAS_ELEMEN:
        if any(k in blob for k in keys):
            tambah(daftar)
    for keys, daftar in LINTAS_RUMPUN:
        if any(k in blob for k in keys):
            tambah(daftar)
            break
    tambah(LINTAS_DEFAULT)
    return hasil[:maksimal]


# ============================================================================
# 3. PRAKTIK PEDAGOGIS (dipilih dari kata kerja Tujuan Pembelajaran)
# ============================================================================

MODEL = {
    "pjbl": {
        "nama": "Pembelajaran Berbasis Proyek (Project Based Learning)",
        "alasan": "Tujuan Pembelajaran menuntut Murid menghasilkan karya/produk nyata",
        "sintaks": [
            ("Penentuan pertanyaan mendasar", "memahami",
             "Guru mengajukan pertanyaan mendasar yang menantang tentang {topik}; "
             "Murid menetapkan wujud karya/produk yang akan dihasilkan."),
            ("Menyusun perencanaan proyek", "memahami",
             "Murid berkelompok menyusun rancangan karya, menentukan kebutuhan alat/bahan, "
             "serta pembagian peran berdasarkan konsep {topik}."),
            ("Menyusun jadwal pelaksanaan", "mengaplikasi",
             "Murid menyusun jadwal kerja dan target tiap pertemuan, lalu menyepakati "
             "kriteria keberhasilan bersama guru."),
            ("Memonitor kemajuan proyek", "mengaplikasi",
             "Murid mengerjakan karya sesuai rancangan; guru mendampingi, mengamati kinerja, "
             "dan memberi umpan balik pada saat proses berjalan."),
            ("Menguji dan menyajikan hasil", "mengaplikasi",
             "Murid menguji hasil karya terhadap kriteria, memperbaiki kekurangan, "
             "lalu menyajikannya di depan kelas atau kepada pihak mitra."),
            ("Evaluasi pengalaman belajar", "merefleksi",
             "Murid mengevaluasi proses dan hasil kerjanya, mengenali kekuatan serta "
             "hambatan yang dialami, dan menyusun rencana perbaikan."),
        ],
    },
    "pbl": {
        "nama": "Pembelajaran Berbasis Masalah (Problem Based Learning)",
        "alasan": "Tujuan Pembelajaran menuntut Murid mendiagnosis dan mengatasi "
                  "gangguan/kerusakan atau memecahkan masalah kerja",
        "sintaks": [
            ("Orientasi Murid pada masalah", "memahami",
             "Guru menyajikan kasus nyata berkaitan dengan {topik}; Murid mengidentifikasi "
             "gejala, data, dan batasan masalah."),
            ("Mengorganisasikan Murid untuk belajar", "memahami",
             "Murid berkelompok memetakan pengetahuan yang sudah dimiliki dan yang masih "
             "perlu dicari untuk menyelesaikan kasus tersebut."),
            ("Membimbing penyelidikan", "mengaplikasi",
             "Murid mengumpulkan data melalui pemeriksaan/pengukuran, penelusuran sumber, "
             "dan pengujian dugaan penyebab secara berurutan."),
            ("Mengembangkan dan menyajikan hasil karya", "mengaplikasi",
             "Murid menetapkan tindakan penyelesaian, melaksanakannya sesuai prosedur, "
             "lalu menyajikan laporan hasil beserta bukti pendukung."),
            ("Menganalisis dan mengevaluasi proses", "merefleksi",
             "Murid meninjau kembali ketepatan langkah penyelesaian, menilai efektivitas "
             "kerja kelompok, dan merumuskan tindak lanjut."),
        ],
    },
    "praktik": {
        "nama": "Pembelajaran Berbasis Praktik dengan Demonstrasi Terbimbing "
                "(pola Teaching Factory sederhana)",
        "alasan": "Tujuan Pembelajaran menuntut penguasaan keterampilan dan prosedur "
                  "kerja sesuai standar",
        "sintaks": [
            ("Persiapan", "memahami",
             "Guru menjelaskan tujuan, standar hasil, serta prosedur keselamatan; Murid "
             "menyiapkan alat, bahan, dan jobsheet untuk {topik}."),
            ("Peragaan/demonstrasi", "memahami",
             "Guru memperagakan langkah kerja {topik} secara bertahap sambil menjelaskan "
             "alasan setiap langkah (pengetahuan esensial dan aplikatif)."),
            ("Peniruan terbimbing", "mengaplikasi",
             "Murid menirukan langkah kerja secara berkelompok dengan pendampingan guru; "
             "kesalahan langsung dikoreksi melalui umpan balik singkat."),
            ("Praktik mandiri berulang", "mengaplikasi",
             "Murid melaksanakan pekerjaan secara mandiri sesuai jobsheet, mencatat hasil "
             "pengukuran/pemeriksaan, dan menjaga penerapan K3 serta kebersihan area kerja."),
            ("Evaluasi dan umpan balik", "merefleksi",
             "Murid membandingkan hasil kerjanya dengan kriteria pada rubrik, menerima "
             "umpan balik, dan menetapkan bagian yang perlu dilatih ulang."),
        ],
    },
    "inkuiri": {
        "nama": "Pembelajaran Berbasis Inkuiri (Inquiry Based Learning)",
        "alasan": "Tujuan Pembelajaran menuntut Murid menggali, mengidentifikasi, "
                  "dan menganalisis konsep",
        "sintaks": [
            ("Orientasi", "memahami",
             "Guru memberikan stimulus berupa {stimulus} untuk membangkitkan rasa ingin "
             "tahu, lalu bersama Murid menetapkan lingkup {topik} yang akan dieksplorasi."),
            ("Merumuskan masalah", "memahami",
             "Murid menyusun pertanyaan yang akan dijawab selama pembelajaran dan "
             "mengemukakan dugaan awal berdasarkan pengalamannya."),
            ("Pengumpulan data", "mengaplikasi",
             "Murid mengumpulkan informasi dari {sumber} serta mencatat temuan pada LKPD."),
            ("Pengolahan dan analisis data", "mengaplikasi",
             "Murid mengelompokkan dan menafsirkan temuan melalui peta konsep, tabel, "
             "atau diagram, kemudian mendiskusikan hubungan antarkonsep."),
            ("Menarik kesimpulan", "mengaplikasi",
             "Murid menyusun kesimpulan berbasis bukti dan menghubungkannya dengan "
             "penerapan nyata {topik} di dunia kerja/kehidupan sehari-hari."),
            ("Komunikasi", "mengaplikasi",
             "Murid menyajikan hasil temuan melalui presentasi kelompok, poster, atau "
             "laporan tertulis, dan menanggapi pertanyaan kelompok lain."),
            ("Refleksi", "merefleksi",
             "Murid merefleksikan apa yang telah dipahami, kesulitan yang dialami, dan "
             "cara memperbaiki cara belajarnya; guru memberi umpan balik konstruktif."),
            ("Aplikasi dan tindak lanjut", "merefleksi",
             "Murid merencanakan penerapan hasil belajar pada konteks nyata dan menetapkan "
             "target belajar untuk pertemuan berikutnya."),
        ],
    },
    "kontekstual": {
        "nama": "Pembelajaran Kontekstual Kolaboratif (pola REACT)",
        "alasan": "Tujuan Pembelajaran menekankan pemahaman konsep yang dikaitkan "
                  "dengan pengalaman nyata Murid",
        "sintaks": [
            ("Relating (mengaitkan)", "memahami",
             "Guru mengaitkan {topik} dengan pengalaman dan lingkungan Murid melalui "
             "{stimulus}."),
            ("Experiencing (mengalami)", "memahami",
             "Murid melakukan pengamatan/percobaan sederhana untuk membangun pemahaman "
             "konsep {topik} dari pengalaman langsung."),
            ("Applying (menerapkan)", "mengaplikasi",
             "Murid menerapkan konsep pada tugas/kasus baru yang lebih kompleks."),
            ("Cooperating (bekerja sama)", "mengaplikasi",
             "Murid bekerja dalam kelompok, saling menjelaskan, dan menyajikan hasil kerja "
             "bersama."),
            ("Transferring (mentransfer)", "merefleksi",
             "Murid merefleksikan pemahamannya dan memindahkan penerapan konsep ke situasi "
             "baru serta menyusun rencana belajar lanjutan."),
        ],
    },
}

KATA_MODEL = [
    ("pjbl", ["merancang", "rancang", "mencipta", "menciptakan", "membuat", "menghasilkan",
              "produk", "karya", "proyek", "mengembangkan", "menata", "komposisi",
              "kreasi", "desain", "menyusun", "memproduksi", "menggambar"]),
    ("pbl", ["memperbaiki", "perbaikan", "perawatan", "merawat", "diagnos", "mendiagnosis",
             "gangguan", "kerusakan", "masalah", "troubleshoot", "mengatasi", "overhaul",
             "servis", "perbaikan ringan", "penyelesaian"]),
    ("praktik", ["mempraktikkan", "praktik", "melakukan", "mengoperasikan", "menggunakan",
                 "prosedur", "memasang", "membongkar", "merakit", "menyetel", "mengukur",
                 "mengelas", "keterampilan", "menerapkan", "pemeriksaan", "memelihara"]),
    ("inkuiri", ["mengidentifikasi", "menganalisis", "mengamati", "menyelidiki",
                 "mengeksplorasi", "memahami", "menjelaskan", "menginterpretasi",
                 "membandingkan", "wawasan", "konsep", "prinsip", "mengklasifikasi",
                 "menyimpulkan", "mengkaji"]),
]

KKO_LEVEL = [
    (["menyebutkan", "menyatakan", "mengingat", "mendaftar"], "C1"),
    (["mengidentifikasi", "menjelaskan", "memahami", "menguraikan", "mendeskripsikan",
      "membedakan", "mengklasifikasi", "menginterpretasi"], "C2"),
    (["menerapkan", "mempraktikkan", "melakukan", "menggunakan", "mengoperasikan",
      "menghitung", "mengukur", "memasang", "membongkar", "merakit", "menyetel",
      "memelihara", "merawat", "menyajikan", "memperagakan"], "C3"),
    (["menganalisis", "mendiagnosis", "memeriksa", "membandingkan", "menelaah",
      "memecahkan", "menyimpulkan"], "C4"),
    (["mengevaluasi", "menilai", "mengkritik", "memvalidasi", "merefleksikan"], "C5"),
    (["merancang", "mencipta", "menciptakan", "membuat", "mengembangkan", "menata",
      "memodifikasi", "menghasilkan", "mengaktualisasikan"], "C6"),
]


def level_kognitif(teks):
    t = teks.lower()
    hasil = "C2"
    for keys, lv in KKO_LEVEL:
        if any(k in t for k in keys):
            hasil = lv
    return hasil


def kko_dominan(teks, jumlah=5):
    t = teks.lower()
    semua = []
    for keys, _ in KKO_LEVEL:
        for k in keys:
            if k in t and k not in semua:
                semua.append(k)
    return semua[:jumlah]


def pilih_model(elemen, cp, tp_teks, watak=None):
    """Menentukan model utama dan model pendukung.

    Untuk mata pelajaran kejuruan, model utama selalu PJBL; model pendukung
    dipilih dari kecenderungan kata kerja Tujuan Pembelajaran agar tetap
    relevan dengan Capaian Pembelajarannya.
    """
    blob = (elemen + " " + cp + " " + tp_teks).lower()
    skor = []
    for kode, keys in KATA_MODEL:
        skor.append((sum(blob.count(k) for k in keys), kode))
    skor.sort(key=lambda x: -x[0])
    if watak in WATAK_PJBL:
        pendukung = "praktik"
        for nilai, kode in skor:
            if kode != "pjbl" and nilai > 0:
                pendukung = kode
                break
        return "pjbl", pendukung
    if skor[0][0] == 0:
        return "kontekstual", None
    utama = skor[0][1]
    pendukung = skor[1][1] if skor[1][0] > 0 else "kontekstual"
    if pendukung == utama:
        pendukung = "kontekstual"
    return utama, pendukung


METODE_DASAR = ["tanya jawab dan curah gagasan (brainstorming)",
                "diskusi kelompok kecil",
                "penugasan melalui LKPD",
                "presentasi hasil kerja kelompok",
                "refleksi terbimbing"]
METODE_PRAKTIK = ["demonstrasi oleh guru",
                  "praktik terbimbing dan praktik mandiri berulang",
                  "penugasan melalui jobsheet",
                  "observasi kinerja saat proses berlangsung",
                  "diskusi kelompok dan presentasi hasil kerja",
                  "refleksi terbimbing"]


def praktik_pedagogis(watak, elemen, cp, tp_teks):
    utama, pendukung = pilih_model(elemen, cp, tp_teks, watak)
    m = MODEL[utama]
    kko = kko_dominan(tp_teks)
    baris = []
    baris.append("**Model:** %s" % m["nama"])
    if pendukung:
        baris.append("**Model/strategi pendukung:** %s" % MODEL[pendukung]["nama"])
    baris.append("")
    baris.append("**Strategi:** pembelajaran berdiferensiasi (penyesuaian tingkat bantuan "
                 "dan kompleksitas tugas), pembelajaran kolaboratif dalam kelompok "
                 "heterogen, serta pendampingan bertahap (scaffolding) yang dikurangi "
                 "secara berangsur.")
    baris.append("")
    metode = METODE_PRAKTIK if watak in PRAKTIS else METODE_DASAR
    baris.append("**Metode:** " + "; ".join(metode) + ".")
    baris.append("")
    if watak in WATAK_PJBL:
        dasar = "**Dasar pemilihan:** " + ALASAN_PJBL_KEJURUAN + "."
    else:
        dasar = "**Dasar pemilihan:** " + m["alasan"] + "."
    if kko:
        dasar += (" Tujuan Pembelajaran pada elemen ini didominasi kata kerja "
                  "operasional \u201c%s\u201d (level kognitif dominan %s), sehingga "
                  "pembelajaran diarahkan pada aktivitas nyata, bukan penyampaian "
                  "materi satu arah." % (", ".join(kko), level_kognitif(tp_teks)))
    baris.append(dasar)
    return "\n".join(baris), utama, m


# ============================================================================
# 4. PEMANFAATAN DIGITAL & LINGKUNGAN
# ============================================================================


def pemanfaatan_digital(media_list, watak):
    per = ("**Tahap perencanaan:** penyiapan bahan ajar visual (slide/infografis), "
           "penyiapan kelas digital pada LMS (Google Classroom), serta penelusuran "
           "sumber dan referensi mutakhir yang relevan.")
    pel = ("**Tahap pelaksanaan:** pemanfaatan " +
           ", ".join(media_list[:4]) +
           "; penayangan video pembelajaran dan animasi/simulasi proses; "
           "pemanfaatan LKPD digital agar Murid dapat mengunggah bukti kerja.")
    if watak in PRAKTIS:
        pel += (" Murid mendokumentasikan proses kerja dengan gawai sebagai bukti "
                "kinerja dan bahan refleksi.")
    ase = ("**Tahap asesmen:** kuis diagnostik dan tes formatif interaktif melalui "
           "Google Form/Quizizz, pengumpulan portofolio digital hasil kerja Murid, "
           "serta rekap nilai pada lembar kerja digital.")
    etika = ("**Catatan etika digital:** Murid dibiasakan menyebutkan sumber, menjaga "
             "data pribadi, dan menggunakan perangkat digital secara bertanggung jawab.")
    return "\n".join([per, "", pel, "", ase, "", etika])


def lingkungan_pembelajaran(watak):
    budaya = ("**Budaya belajar:** suasana yang aman, nyaman, dan saling memuliakan; "
              "kesepakatan kelas disusun bersama Murid; setiap pertanyaan dan kesalahan "
              "diperlakukan sebagai bagian wajar dari proses belajar; guru memberi "
              "penghargaan atas usaha, bukan hanya hasil.")
    fisik = "**Ruang fisik:** " + RUANG_FISIK.get(watak, RUANG_FISIK["umum"]) + "."
    if watak in PRAKTIS:
        fisik += (" Penataan kelompok kerja diatur agar seluruh Murid dapat mengamati "
                  "peragaan dan bergerak dengan aman.")
    virtual = ("**Ruang virtual:** kelas digital pada LMS (Google Classroom) untuk "
               "pembagian bahan ajar, pengumpulan tugas, dan umpan balik; grup kelas "
               "untuk pengumuman; penyimpanan awan (cloud) untuk portofolio digital Murid.")
    return "\n".join([budaya, "", fisik, "", virtual])


# ============================================================================
# 5. ASESMEN
# ============================================================================


def teknik_asesmen(watak):
    if watak == "kejuruan":
        return {"akhir_nama": "Penilaian Kinerja (Unjuk Kerja) dan Laporan Hasil Kerja",
                "akhir_teknik": "penilaian kinerja praktik menggunakan rubrik unjuk kerja, "
                                "dilengkapi laporan/jobsheet hasil kerja dan tes tertulis "
                                "singkat untuk pengetahuan esensial",
                "bentuk": "unjuk kerja + uraian singkat"}
    if watak == "seni":
        return {"akhir_nama": "Penilaian Kinerja (Penyajian Karya) dan Penilaian Produk",
                "akhir_teknik": "penilaian penyajian/pertunjukan karya menggunakan rubrik, "
                                "dilengkapi penilaian produk karya dan lembar apresiasi tertulis",
                "bentuk": "unjuk karya + uraian"}
    if watak == "olahraga":
        return {"akhir_nama": "Penilaian Kinerja Gerak dan Tes Praktik",
                "akhir_teknik": "penilaian kinerja gerak menggunakan rubrik teknik dan "
                                "tes praktik terukur",
                "bentuk": "unjuk kerja gerak"}
    if watak == "informatika":
        return {"akhir_nama": "Penilaian Produk Digital dan Tes Tertulis",
                "akhir_teknik": "penilaian produk digital (program/karya) menggunakan rubrik "
                                "dan tes tertulis bentuk uraian",
                "bentuk": "produk + uraian"}
    if watak == "bahasa":
        return {"akhir_nama": "Tes Tertulis dan Penilaian Portofolio",
                "akhir_teknik": "tes tertulis (uraian) dan penilaian portofolio karya "
                                "tulis/lisan Murid",
                "bentuk": "uraian + portofolio"}
    return {"akhir_nama": "Tes Tertulis dan Penilaian Portofolio",
            "akhir_teknik": "tes tertulis bentuk pilihan ganda dan uraian, dilengkapi "
                            "penilaian portofolio hasil belajar",
            "bentuk": "pilihan ganda + uraian"}


def teks_asesmen(watak, tk):
    awal = ("**Fungsi:** *assessment for learning* \u2014 memetakan kesiapan belajar Murid.\n"
            "**Teknik:** pertanyaan diagnostik lisan dan kuis singkat non-nilai "
            "(dapat melalui Google Form) pada awal pertemuan pertama.\n"
            "**Instrumen:** lembar pertanyaan diagnostik \u2014 **Lampiran 1**.\n"
            "**Tindak lanjut:** hasil dipakai untuk mengelompokkan Murid \u2014 kelompok "
            "perlu pendampingan mendapat bantuan bertahap dan contoh tambahan, "
            "kelompok siap mendapat tugas pengayaan dan peran sebagai tutor sebaya.")
    proses = ("**Fungsi:** *assessment as learning* dan *assessment for learning* \u2014 "
              "Murid menilai proses belajarnya sendiri dan guru memperbaiki pembelajaran "
              "selama proses berjalan.\n"
              "**Teknik:** observasi kinerja/sikap saat kegiatan berlangsung, umpan balik "
              "formatif lisan pada saat itu juga, penilaian diri, dan penilaian antarteman "
              "(*peer assessment*), serta jurnal refleksi singkat pada akhir setiap pertemuan.\n"
              "**Instrumen:** rubrik observasi kinerja \u2014 **Lampiran 2**; lembar penilaian "
              "diri, penilaian antarteman, dan jurnal refleksi \u2014 **Lampiran 4**.\n"
              "**Tindak lanjut:** Murid yang belum mencapai kriteria mendapat latihan ulang "
              "terbimbing pada bagian yang belum dikuasai sebelum asesmen akhir.")
    akhir = ("**Fungsi:** *assessment of learning* \u2014 mengukur ketercapaian Tujuan "
             "Pembelajaran pada elemen ini.\n"
             "**Teknik:** %s.\n"
             "**Instrumen:** kisi-kisi, butir asesmen, rubrik penskoran, dan daftar nilai "
             "\u2014 **Lampiran 3**.\n"
             "**Kriteria ketercapaian:** Murid dinyatakan mencapai Tujuan Pembelajaran "
             "apabila memperoleh nilai \u2265 75 (predikat Cukup ke atas).\n"
             "**Tindak lanjut:** nilai < 75 mengikuti pembelajaran remedial pada indikator "
             "yang belum tuntas; nilai \u2265 90 mendapat pengayaan berupa tugas dengan "
             "tingkat kesulitan lebih tinggi." % tk["akhir_teknik"])
    return awal, proses, akhir


# ============================================================================
# 6. PENULISAN DOKUMEN
# ============================================================================


def _para_rich(p, teks, size, bold_semua=False):
    bagian = re.split(r"(\*\*[^*]+\*\*|\*[^*]+\*)", teks)
    ada = False
    for b in bagian:
        if not b:
            continue
        ada = True
        if b.startswith("**") and b.endswith("**"):
            style_run(p.add_run(b[2:-2]), size, True)
        elif b.startswith("*") and b.endswith("*") and len(b) > 2:
            r = p.add_run(b[1:-1])
            style_run(r, size, bold_semua)
            r.italic = True
        else:
            style_run(p.add_run(b), size, bold_semua)
    if not ada:
        style_run(p.add_run(""), size, bold_semua)


def sel(cell, teks, align=J, size=9, bold=False):
    for p in list(cell.paragraphs):
        p._element.getparent().remove(p._element)
    for ln in (teks.split("\n") if teks else [""]):
        p = cell.add_paragraph()
        p.alignment = align
        p.paragraph_format.space_after = Pt(0)
        p.paragraph_format.space_before = Pt(0)
        n_spasi = len(ln) - len(ln.lstrip(" "))
        if n_spasi >= 3:
            p.paragraph_format.left_indent = Cm(0.4 * (n_spasi // 3))
        _para_rich(p, ln.strip() if n_spasi else ln, size, bold)


def paragraf(doc, teks, size=11, bold=False, align=C, after=0):
    p = doc.add_paragraph()
    p.alignment = align
    p.paragraph_format.space_after = Pt(after)
    p.paragraph_format.space_before = Pt(0)
    _para_rich(p, teks, size, bold)
    return p


LEBAR_HALAMAN = 17.0     # lebar area cetak A4 potret dengan tepi 2 cm
LEBAR_MINIMUM = 0.55     # lebar kolom terkecil yang masih terbaca


def rapikan_lebar(w, total=LEBAR_HALAMAN):
    """Pastikan lebar kolom selalu positif dan jumlahnya tidak melampaui halaman.

    Diperlukan karena jumlah butir asesmen mengikuti jumlah Tujuan Pembelajaran,
    yang pada mata pelajaran tertentu bisa mencapai belasan butir.
    """
    w = [max(LEBAR_MINIMUM, float(x)) for x in w]
    jml = sum(w)
    if jml > total:
        w = [x * total / jml for x in w]
    return w


def lebar(tabel, w):
    tabel.autofit = False
    w = rapikan_lebar(w)
    for row in tabel.rows:
        for i, cm in enumerate(w):
            if i < len(row.cells):
                row.cells[i].width = Cm(cm)
    for i, cm in enumerate(w):
        if i < len(tabel.columns):
            tabel.columns[i].width = Cm(cm)


def tabel_label(doc, baris, w=(4.6, 12.4), size=9, warna="F2F2F2"):
    """Tabel 2 kolom: label (kiri, abu) + isi."""
    t = doc.add_table(rows=len(baris), cols=2)
    t.style = "Table Grid"
    t.alignment = WD_TABLE_ALIGNMENT.CENTER
    for i, (lab, isi) in enumerate(baris):
        sel(t.cell(i, 0), lab, L, size, True)
        shade(t.cell(i, 0), warna)
        sel(t.cell(i, 1), isi, J, size)
    lebar(t, w)
    return t


def tabel_data(doc, header, baris, w, size=8.5, align=None, warna="D9E2F3"):
    t = doc.add_table(rows=len(baris) + 1, cols=len(header))
    t.style = "Table Grid"
    t.alignment = WD_TABLE_ALIGNMENT.CENTER
    for j, h in enumerate(header):
        sel(t.cell(0, j), h, C, size, True)
        shade(t.cell(0, j), warna)
    for i, row in enumerate(baris):
        for j, v in enumerate(row):
            a = align[j] if align else (C if j == 0 else L)
            sel(t.cell(i + 1, j), v, a, size)
    lebar(t, w)
    return t


def judul_bagian(doc, teks):
    p = doc.add_paragraph()
    p.alignment = L
    p.paragraph_format.space_before = Pt(8)
    p.paragraph_format.space_after = Pt(3)
    style_run(p.add_run(teks), 11, True)


# ============================================================================
# 7. PENYUSUN ISI SATU RPM
# ============================================================================


def bersih_bullet(teks):
    if isinstance(teks, (list, tuple)):
        teks = "\n".join(teks)
    out = []
    for ln in (teks or "").split("\n"):
        ln = ln.strip()
        ln = re.sub(r"^[\u2022\-\*]\s*", "", ln)
        ln = re.sub(r"^\d+\.\s*", "", ln)
        if ln:
            out.append(ln)
    return out


AWALAN_KERJA = ("me", "ber", "ter", "mem", "men", "meng", "meny", "mer", "pe")


def _kata_kerja(frasa):
    """Ambil kata kerja pertama (berawalan me-/ber-) dari sebuah frasa."""
    for w in re.findall(r"[A-Za-z\-]+", frasa):
        wl = w.lower()
        if wl.startswith(("meng", "meny", "mem", "men", "mer", "me", "ber")) and len(wl) > 5:
            return wl
    return None


def rapikan_tp(tp_items):
    """Melengkapi Tujuan Pembelajaran yang kehilangan kata kerja.

    Capaian Pembelajaran sering berupa satu kalimat panjang. Ketika dipecah
    menjadi beberapa Tujuan Pembelajaran, potongan kedua dan seterusnya bisa
    kehilangan kata kerjanya (mis. "Murid mampu perubahan iklim ..."). Fungsi ini
    menyisipkan kembali kata kerja dari potongan sebelumnya sehingga rumusan
    Tujuan Pembelajaran tetap utuh dan tidak rancu.
    """
    hasil = []
    kerja_terakhir = None
    for item in tp_items:
        m = re.match(r"^(Murid mampu)\s+(.*)$", item.strip(), flags=re.I)
        if not m:
            hasil.append(item)
            continue
        depan, sisa = m.group(1), m.group(2).strip()
        kk = _kata_kerja(sisa.split(",")[0][:60])
        kata1 = re.findall(r"[A-Za-z\-]+", sisa)
        kata1 = kata1[0].lower() if kata1 else ""
        punya = bool(kk) and sisa.lower().startswith(kk)
        if punya:
            kerja_terakhir = kk
        elif kerja_terakhir:
            sisa = "%s %s" % (kerja_terakhir, sisa)
        else:
            sisa = "menjelaskan %s" % sisa
        hasil.append("%s %s" % (depan, sisa))
    return hasil


def agenda_pertemuan(tp_items, n_tm):
    """Sebar Tujuan Pembelajaran ke sejumlah pertemuan hasil ATP."""
    n = len(tp_items)
    hasil = []
    if n == 0:
        return hasil
    if n_tm >= n:
        bagi = bagi_minggu([1] * n, n_tm)
        awal = 1
        for i, k in enumerate(bagi):
            akhir = awal + k - 1
            lab = ("Pertemuan %d" % awal) if k == 1 else ("Pertemuan %d\u2013%d" % (awal, akhir))
            hasil.append((lab, [tp_items[i]], k))
            awal = akhir + 1
    else:
        bagi = bagi_minggu([1] * n_tm, n)
        idx = 0
        for i, k in enumerate(bagi):
            hasil.append(("Pertemuan %d" % (i + 1), tp_items[idx:idx + k], 1))
            idx += k
    return hasil


def ringkas(teks, batas=90):
    t = re.sub(r"\s+", " ", teks).strip(" .;")
    if len(t) <= batas:
        return t
    return t[:batas].rsplit(" ", 1)[0] + "\u2026"


def topik_utama(elemen):
    return re.sub(r"\s*\([^)]*\)", "", elemen).strip()


# ----------------------------------------------------------------------------
# UNIT RPM BERBASIS TUJUAN PEMBELAJARAN
# ----------------------------------------------------------------------------


def daftar_tp(detail_list):
    """Susun daftar Tujuan Pembelajaran bernomor dari beberapa elemen.

    Nomor berjalan lurus (1, 2, 3, ...) melintasi elemen supaya guru bisa
    menunjuk TP mana saja yang mau dijadikan satu RPM.
    """
    out = []
    for d in detail_list:
        sumber = d.get("tp_list") or d.get("tp")
        if isinstance(sumber, (list, tuple)):
            sumber = "\n".join(sumber)
        butir = rapikan_tp(bersih_bullet(sumber))
        # Buang butir yang rumusannya sama setelah dirapikan, agar tidak ada
        # Tujuan Pembelajaran yang muncul dua kali dalam satu berkas RPM.
        bersih, sudah = [], set()
        for b in butir:
            kunci = re.sub(r"[^a-z0-9 ]", "", b.lower()).strip()
            if kunci and kunci in sudah:
                continue
            sudah.add(kunci)
            bersih.append(b)
        for b in bersih:
            out.append({"no": len(out) + 1, "tp": b, "det": d,
                        "elemen": d["elemen"], "cp": d["cp"],
                        "semester": d["semester"], "label_semester": d["label_semester"],
                        "jenis": d["jenis"], "urut_kelas": d["urut_kelas"]})
    return out


def urai_pilihan(teks, maksimal):
    """Ubah tulisan seperti "1,2,4" atau "1-5" atau "semua" menjadi daftar nomor."""
    teks = (teks or "").strip().lower()
    if teks in ("", "semua", "all", "*"):
        return list(range(1, maksimal + 1))
    nomor = []
    for bagian in re.split(r"[,\s]+", teks):
        if not bagian:
            continue
        m = re.match(r"^(\d+)\s*[-\u2013]\s*(\d+)$", bagian)
        if m:
            a, b = int(m.group(1)), int(m.group(2))
            nomor.extend(range(min(a, b), max(a, b) + 1))
            continue
        if bagian.isdigit():
            nomor.append(int(bagian))
            continue
        return None, "Tulisan '%s' tidak dikenali." % bagian
    urut = []
    for n in nomor:
        if n < 1 or n > maksimal:
            return None, "Nomor TP %d di luar jangkauan (tersedia 1\u2013%d)." % (n, maksimal)
        if n not in urut:
            urut.append(n)
    if not urut:
        return None, "Belum ada nomor TP yang dipilih."
    return urut, None


def bagi_rata(jumlah, kelompok):
    """Bagi `jumlah` butir ke `kelompok` wadah; sisa diberikan ke wadah terdepan."""
    if kelompok <= 0:
        return []
    dasar = jumlah // kelompok
    sisa = jumlah % kelompok
    return [dasar + (1 if i < sisa else 0) for i in range(kelompok)]


def kelompok_pertemuan(entri, n_tm=None, tp_per_tm=1):
    """Kelompokkan Tujuan Pembelajaran ke dalam pertemuan menurut ATP.

    Jumlah pertemuan (`n_tm`) diambil TETAP dari basis data ATP, sehingga JP
    pada RPM tidak pernah berselisih dengan ATP. Yang menyesuaikan diri adalah
    pengelompokan TP:

    * TP lebih banyak daripada pertemuan  -> satu pertemuan memuat beberapa TP
      (misalnya 3 TP dalam 1 pertemuan, sesuai kedekatan topiknya);
    * pertemuan lebih banyak daripada TP  -> satu TP menempati rentang
      pertemuan, misalnya "Pertemuan 3–4" (bukan TP yang sama diulang
      sebagai dua pertemuan terpisah).

    Hasil: daftar (label, daftar_TP, jumlah_pertemuan).
    """
    n = len(entri)
    if n == 0:
        return []
    if n_tm and int(n_tm) > 0:
        pola = agenda_pertemuan(list(range(n)), int(n_tm))
        return [(lab, [entri[i] for i in idx], k) for lab, idx, k in pola]
    tp_per_tm = max(1, int(tp_per_tm or 1))
    hasil, i = [], 0
    while i < n:
        hasil.append(("Pertemuan %d" % (len(hasil) + 1), entri[i:i + tp_per_tm], 1))
        i += tp_per_tm
    return hasil


def gabung_unik(daftar_teks):
    """Gabungkan beberapa blok berbutir menjadi satu daftar tanpa pengulangan."""
    out = []
    for teks in daftar_teks:
        for b in bersih_bullet(teks):
            if b not in out:
                out.append(b)
    return out


# Frasa tahap kognitif (dipakai saat CP perlu dipecah lebih rinci). Frasa ini
# BUKAN materi, jadi tidak boleh dipakai sebagai nama Materi Pokok; materinya
# ada pada bagian sesudah "pada"/"mengenai".
FRASA_TAHAP = ("bagian, istilah", "bagian istilah", "prinsip dan cara kerja",
               "langkah kerja", "permasalahan dan penyebab",
               "hasil kerja serta", "hasil kerja dan laporan", "hasil kerja",
               "konsep kunci")


def nama_topik(butir, nomor=1):
    """Nama Materi Pokok diambil dari pokok materi Tujuan Pembelajaran pertama.

    Tidak ada topik baru yang dikarang: namanya adalah objek/pokok materi yang
    memang tertulis pada rumusan TP, dan rumusan TP itu sendiri diturunkan dari
    naskah Capaian Pembelajaran.

    Untuk TP yang memakai frasa tahap kognitif \u2014 mis. "mengidentifikasi bagian,
    istilah, dan konsep kunci pada gambar teknik otomotif" \u2014 yang diambil adalah
    materinya ("gambar teknik otomotif"), bukan frasa tahapannya.
    """
    if not butir:
        return "Materi Pokok %d" % nomor
    inti = pokok_tp(butir[0]["tp"]).strip()
    rendah = inti.lower()
    if any(rendah.startswith(f) for f in FRASA_TAHAP):
        pisah = re.split(r"\s+(?:pada|mengenai|tentang)\s+", inti, maxsplit=1)
        if len(pisah) > 1 and len(pisah[1]) > 8:
            inti = pisah[1]
    else:
        inti = re.split(r"\s+(?:dalam|untuk|sesuai|melalui)\s+", inti)[0]
    inti = re.sub(r"[.;,]+$", "", inti).strip()
    if len(inti) < 8:
        inti = topik_utama(butir[0]["elemen"])
    if len(inti) > 62:
        inti = inti[:60].rsplit(" ", 1)[0]
    return kapital(inti)


# Kata penghubung yang tidak layak mengawali nama Materi Pokok.
AWALAN_BUANG = ("dan", "serta", "atau", "termasuk", "meliputi", "yaitu",
                "antara", "berdasarkan", "sesuai", "pada", "dalam", "untuk")
# Penanda bahasa Indonesia untuk menolak potongan berbahasa Inggris.
PENANDA_ID = (" dan ", " yang ", " pada ", " serta ", " dengan ", " untuk ",
              " dalam ", " atau ", " sesuai ", " dari ", " secara ", " tentang ")


def _nama_layak(x):
    """Rapikan satu calon nama Materi Pokok; kembalikan None bila tidak layak.

    Yang ditolak: potongan berbahasa Inggris (terjemahan di dalam dokumen CP),
    potongan yang hanya berupa sambungan kalimat, dan potongan terlalu pendek.
    """
    x = re.sub(r"^[\u2022\-\s]+", "", x or "")
    x = re.sub(r"\s+", " ", x).strip(" .;,")
    if x.startswith("("):
        return None
    x = re.sub(r"\s*\([^)]*\)", "", x).strip(" .;,")
    kata = x.split(" ")
    while kata and kata[0].lower().strip(",") in AWALAN_BUANG:
        kata = kata[1:]
    x = " ".join(kata).strip(" .;,")
    if len(x) < 12:
        return None
    if len(x) > 25 and not any(p in " " + x.lower() + " " for p in PENANDA_ID):
        # tidak ada satu pun penanda bahasa Indonesia -> kemungkinan terjemahan
        return None
    if len(x) > 62:
        x = x[:60].rsplit(" ", 1)[0]
    return kapital(x)


def kandidat_topik(det):
    """Daftar calon nama Materi Pokok untuk satu CP.

    Sumbernya kolom "Materi Pokok" pada ATP mata pelajaran yang sama, sehingga
    nama topik di RPM sama dengan yang tertulis di ATP. Calon yang berupa
    potongan kalimat atau terjemahan bahasa Inggris disaring lebih dulu.
    """
    if not det:
        return []
    out = []
    for x in bersih_bullet(det.get("materi") or ""):
        nm = _nama_layak(x)
        if nm and nm.lower() not in [y.lower() for y in out]:
            out.append(nm)
    return out


def bagi_topik(entri, n_tm, n_topik=None, det=None):
    """Pecah Tujuan Pembelajaran satu CP menjadi beberapa Materi Pokok.

    Satu Materi Pokok akan menjadi satu berkas RPM yang memuat beberapa
    pertemuan. Jatah pertemuan tiap Materi Pokok dihitung sebanding dengan
    jumlah TP di dalamnya, dan jumlah seluruhnya tetap sama dengan jumlah
    pertemuan CP itu menurut ATP \u2014 supaya ATP dan RPM tidak berselisih.

    Bila `n_topik` tidak diisi, pembagian dibuat otomatis kira-kira 3 pertemuan
    per Materi Pokok.
    """
    n = len(entri)
    if n == 0:
        return []
    n_tm = max(1, int(n_tm or 1))
    maks = min(n, n_tm)
    kand = kandidat_topik(det)
    if n_topik in (None, 0, "", "0"):
        # Patokan otomatis: satu Materi Pokok kira-kira 3 pertemuan, sehingga
        # satu CP dengan 7 pertemuan menjadi 3 berkas (3 + 2 + 2 pertemuan).
        n_topik = int(math.ceil(n_tm / 3.0)) or 1
    n_topik = max(1, min(int(n_topik), maks))

    ukuran = bagi_rata(n, n_topik)
    jatah, sisa = [], n_tm
    for i, u in enumerate(ukuran):
        tersisa_grup = len(ukuran) - i - 1
        if tersisa_grup == 0:
            jatah.append(max(1, sisa))
            break
        v = int(round(float(n_tm) * u / n)) or 1
        v = max(1, min(v, sisa - tersisa_grup))
        jatah.append(v)
        sisa -= v

    out, mulai, dipakai = [], 0, []
    for i, u in enumerate(ukuran):
        butir = entri[mulai:mulai + u]
        mulai += u
        nm = kand[i] if i < len(kand) else (_nama_layak(nama_topik(butir, i + 1))
                                            or nama_topik(butir, i + 1))
        # nama dasar dipendekkan lebih dulu agar penanda "(Bagian n)" utuh
        asal = nm if len(nm) <= 48 else nm[:46].rsplit(" ", 1)[0]
        urut = 2
        while nm.lower() in [y.lower() for y in dipakai]:
            nm = "%s (Bagian %d)" % (asal, urut)
            urut += 1
        dipakai.append(nm)
        out.append({"butir": butir, "tm": jatah[i], "nama": nm})
    return out


def bangun_unit(entri, meta, n_tm=None, tp_per_tm=1, nomor_unit=1, topik=None):
    """Bentuk satu unit RPM dari sekumpulan Tujuan Pembelajaran terpilih.

    Alokasi waktu unit dihitung dari jumlah pertemuan × JP per pertemuan,
    memakai angka yang sama dengan ATP.
    """
    grup = kelompok_pertemuan(entri, n_tm=n_tm, tp_per_tm=tp_per_tm)
    # Jumlah pertemuan dijumlahkan dari jatah tiap kelompok TP supaya sama
    # dengan jumlah pertemuan pada ATP (satu kelompok bisa 2–3 pertemuan).
    tm = sum(g[2] for g in grup)
    jp = tm * meta["jpp"]
    minggu = float(jp) / meta["jp"] if meta["jp"] else 0

    dets, elemen_nama, cp_teks = [], [], []
    for e in entri:
        if e["det"] not in dets:
            dets.append(e["det"])
        if e["elemen"] not in elemen_nama:
            elemen_nama.append(e["elemen"])
            cp_teks.append(e["cp"])

    utama = dets[0]
    if len(elemen_nama) == 1:
        elemen_teks = elemen_nama[0]
        cp_gabung = cp_teks[0]
    else:
        elemen_teks = "\n".join("%d. %s" % (i + 1, n) for i, n in enumerate(elemen_nama))
        cp_gabung = "\n\n".join("**%s:** %s" % (elemen_nama[i], c) for i, c in enumerate(cp_teks))

    dpl = []
    for d in dets:
        for x in (d.get("dpl") or []):
            if x not in dpl:
                dpl.append(x)

    pert = [(lab, [e["tp"] for e in g], k) for lab, g, k in grup]
    nomor_tp = [e["no"] for e in entri]

    unit = dict(utama)
    unit.update({
        "elemen": elemen_nama[0],
        "elemen_teks": elemen_teks,
        "elemen_nama": elemen_nama,
        "cp": cp_teks[0],
        "cp_teks": cp_gabung,
        "dpl": dpl,
        "materi": "\n".join("\u2022 " + x for x in gabung_unik([d["materi"] for d in dets])),
        "media": "\n".join("\u2022 " + x for x in gabung_unik([d["media"] for d in dets])),
        "atp": "\n".join("%d. %s" % (i + 1, x) for i, x in
                        enumerate(gabung_unik([d["atp"] for d in dets]))),
        "tp": "\n".join(e["tp"] for e in entri),
        "tp_list": [e["tp"] for e in entri],
        "pertemuan": pert,
        "nomor_tp": nomor_tp,
        "tm": tm,
        "jp": jp,
        "minggu": minggu,
        "topik": topik,
        "urut": nomor_unit,
        "alokasi": ("%d JP\n%d pertemuan x %d JP\n= %d menit"
                    % (jp, tm, meta["jpp"], jp * meta["menit"])),
    })
    return unit


def susun_isi(det, meta, s, id_data, urut_prev):
    """Membangun seluruh isi tabel RPM untuk satu elemen/CP."""
    elemen = det["elemen"]
    cp = det["cp"]
    watak = karakter_mapel(s, elemen, cp)
    topik = det.get("topik") or topik_utama(elemen)
    materi_list = bersih_bullet(det["materi"])
    media_list = bersih_bullet(det["media"])
    atp_items = bersih_bullet(det["atp"])
    if det.get("pertemuan"):
        agenda = [(lab, list(items), k) for lab, items, k in det["pertemuan"]]
        tp_items = [x for lab, items, k in det["pertemuan"] for x in items]
    else:
        tp_items = rapikan_tp(bersih_bullet(det.get("tp_list") or det["tp"]))
        agenda = agenda_pertemuan(tp_items, det["tm"])
    ped_teks, kode_model, model = praktik_pedagogis(watak, elemen, cp, det["tp"])
    tk = teknik_asesmen(watak)

    # ---------- IDENTITAS ----------
    alokasi_1 = ("%d JP = %d pertemuan \u00d7 %d JP @ %d menit (total %d menit) \u2014 "
                 "setara %s minggu efektif pada %d JP/minggu"
                 % (det["jp"], det["tm"], meta["jpp"], meta["menit"],
                    det["jp"] * meta["menit"], fmt_minggu(det["minggu"]), meta["jp"]))
    identitas = [
        ("Nama Instansi", id_data["instansi"]),
        ("NPSN", id_data["npsn"]),
        ("Penyusun", "%s  (%s)" % ((id_data.get("penyusun") or "").strip() or TITIK,
                                    npa_teks(id_data.get("npa")))),
        ("Mata Pelajaran", meta["mapel"]),
        ("Fase", "Fase %s" % meta["fase"]),
        ("Kelas", id_data["rombel"]),
        ("Alokasi Waktu", alokasi_1),
        ("Materi Pokok", topik),
    ]

    # ---------- IDENTIFIKASI ----------
    if urut_prev:
        awal_tahu = ("Murid telah mempelajari elemen sebelumnya, yaitu %s, sehingga "
                     "sudah memiliki dasar pengetahuan dan kebiasaan kerja yang dapat "
                     "dijadikan pijakan." % "; ".join(urut_prev))
    else:
        awal_tahu = ("Elemen ini merupakan elemen pertama pada Fase %s, sehingga "
                     "pengetahuan awal Murid umumnya masih berasal dari pengalaman "
                     "sehari-hari dan jenjang sebelumnya, belum dari pembelajaran formal "
                     "pada mata pelajaran ini." % meta["fase"])
    murid_teks = "\n".join([
        "**Pengetahuan awal:** " + awal_tahu + " Pemetaan pengetahuan awal dilakukan "
        "melalui asesmen awal (Lampiran 1) pada pertemuan pertama.",
        "",
        "**Minat dan gaya belajar:** Murid pada kelas ini memiliki kecenderungan minat "
        "yang beragam. Karena itu penyajian materi dilakukan melalui beberapa jalur "
        "sekaligus \u2014 tayangan visual, penjelasan lisan, dan kegiatan langsung "
        "(bergerak/mencoba) \u2014 agar setiap Murid menemukan cara belajar yang cocok.",
        "",
        "**Latar belakang:** Murid berasal dari latar keluarga, sosial, dan kemampuan "
        "ekonomi yang berbeda, termasuk perbedaan akses terhadap perangkat digital. "
        "Tugas berbasis internet karena itu selalu disertai pilihan pengerjaan "
        "luring/berkelompok.",
        "",
        "**Kebutuhan belajar (hasil pemetaan awal dipakai untuk membagi tiga kelompok):**",
        "   \u2022 Perlu pendampingan \u2014 diberi contoh terurai, bantuan langkah demi "
        "langkah, dan pendampingan tutor sebaya.",
        "   \u2022 Cukup \u2014 diberi tugas sesuai tingkat sasaran dengan bantuan "
        "secukupnya yang dikurangi berangsur.",
        "   \u2022 Siap/melampaui \u2014 diberi pengayaan berupa kasus lebih kompleks dan "
        "peran sebagai tutor sebaya.",
        "",
        "**Catatan keberagaman:** Murid dengan hambatan penglihatan/pendengaran atau "
        "kebutuhan khusus lain ditempatkan pada posisi yang memudahkan akses, diberi "
        "bahan cetak berukuran besar, dan diberi tambahan waktu bila diperlukan.",
    ])

    jenis_tahu = []
    jenis_tahu.append("   \u2022 **Pengetahuan esensial:** " +
                      (ringkas(materi_list[0], 160) if materi_list else topik) +
                      " \u2014 konsep, istilah, dan prinsip dasar yang harus dikuasai "
                      "sebagai fondasi elemen berikutnya.")
    if len(materi_list) > 1:
        jenis_tahu.append("   \u2022 **Pengetahuan aplikatif:** " +
                          ringkas("; ".join(materi_list[1:3]), 200) +
                          " \u2014 diterapkan Murid dalam kegiatan/tugas nyata.")
    else:
        jenis_tahu.append("   \u2022 **Pengetahuan aplikatif:** penerapan konsep %s pada "
                          "tugas dan kasus nyata sesuai prosedur yang berlaku." % topik.lower())
    jenis_tahu.append("   \u2022 **Pengetahuan nilai dan karakter:** kejujuran dalam "
                      "melaporkan hasil, tanggung jawab terhadap alat dan lingkungan, "
                      "ketertiban dan kedisiplinan mengikuti prosedur, serta penghargaan "
                      "terhadap hasil kerja orang lain.")

    if watak == "kejuruan":
        sulit = ("Tingkat kesulitan tergolong sedang sampai tinggi karena Murid harus "
                 "memadukan pemahaman konsep dengan ketepatan keterampilan tangan dan "
                 "kepatuhan pada prosedur keselamatan. Titik yang biasanya sulit adalah "
                 "ketelitian pembacaan alat/urutan langkah kerja, sehingga bagian ini "
                 "diberi peragaan ulang dan latihan berulang.")
        relevan = ("Materi ini langsung terpakai pada pekerjaan nyata di bidang keahlian "
                   "yang ditekuni Murid (mata pelajaran %s). Konteks nyata yang dipakai "
                   "sebagai contoh adalah pekerjaan sehari-hari di bengkel/industri, "
                   "standar kerja yang berlaku, dan layanan kepada pelanggan."
                   % meta["mapel"])
    elif watak == "seni":
        sulit = ("Tingkat kesulitan tergolong sedang karena Murid perlu memadukan "
                 "kepekaan rasa, penguasaan teknik, dan keberanian tampil. Bagian yang "
                 "biasanya sulit adalah konsistensi teknik dan kepercayaan diri saat "
                 "menyajikan karya, sehingga diberi latihan bertahap dan penampilan "
                 "kelompok terlebih dahulu.")
        relevan = ("Materi ini relevan dengan kehidupan Murid sebagai bekal berekspresi, "
                   "melestarikan tradisi setempat, dan membaca makna karya seni di "
                   "lingkungan sekitar.")
    else:
        sulit = ("Tingkat kesulitan tergolong sedang. Bagian yang umumnya menuntut waktu "
                 "lebih banyak adalah penerapan konsep pada situasi baru, sehingga "
                 "disediakan contoh terurai dan latihan bertahap.")
        relevan = ("Materi ini relevan dengan kehidupan Murid karena dipakai untuk "
                   "memahami dan menyikapi persoalan nyata di sekitarnya, serta menjadi "
                   "bekal pada jenjang dan dunia kerja berikutnya.")

    materi_teks = "\n".join([
        "**Jenis pengetahuan:**",
    ] + jenis_tahu + [
        "",
        "**Relevansi dengan kehidupan nyata:** " + relevan,
        "",
        "**Tingkat kesulitan:** " + sulit,
        "",
        "**Struktur materi (urutan dari mudah ke sulit, sesuai Alur Tujuan Pembelajaran):**",
    ] + ["   %d. %s" % (i + 1, ringkas(a, 170)) for i, a in enumerate(atp_items)] + [
        "",
        "**Integrasi nilai dan karakter:** ditanamkan melalui kebiasaan kerja sehari-hari "
        "di kelas \u2014 datang tepat waktu, menyiapkan dan mengembalikan alat, bekerja "
        "aman, jujur mencatat hasil, dan saling menghargai pendapat.",
    ])

    dpl_dipilih = det["dpl"]
    nama_dpl = [n for n, _ in DPL]
    dpl_baris = []
    for i, n in enumerate(nama_dpl):
        tanda = "\u2611" if n in dpl_dipilih else "\u2610"
        dpl_baris.append("%s  DPL%d. %s" % (tanda, i + 1, n))
    dpl_teks = "\n".join(dpl_baris)
    dpl_alasan = ("**Alasan pemilihan:** dimensi yang ditandai adalah dimensi yang benar-"
                  "benar dilatih dan dapat diamati pada elemen ini, diturunkan dari kata "
                  "kunci Capaian Pembelajaran dan bentuk kegiatan yang dipakai. Dimensi "
                  "lain tidak ditandai agar penilaian tetap terfokus dan tidak mengada-ada.")

    # ---------- DESAIN PEMBELAJARAN ----------
    ld = lintas_disiplin(s["nama_asli"], elemen, cp)
    ld_teks = "\n".join(["   \u2022 **%s:** %s." % (n, k) for n, k in ld])

    tp_teks_baris = []
    nomor = 1
    for lab, items, _ in agenda:
        tp_teks_baris.append("**%s**" % lab)
        for it in items:
            tp_teks_baris.append("   %d. %s" % (nomor, it if it.endswith(".") else it + "."))
            nomor += 1
        tp_teks_baris.append("")
    if tp_teks_baris and tp_teks_baris[-1] == "":
        tp_teks_baris.pop()
    tp_final = "\n".join(tp_teks_baris)

    topik_teks = "\n".join([
        "**Topik pokok:** " + topik,
        "",
        "**Sub-topik (kontekstual, digali dari Capaian Pembelajaran):**",
    ] + ["   \u2022 %s" % ringkas(m, 170) for m in materi_list])

    kem_teks = "\n".join(KEMITRAAN.get(watak, KEMITRAAN["umum"]))
    ling_teks = lingkungan_pembelajaran(watak)
    dig_teks = pemanfaatan_digital(media_list, watak)

    # ---------- PENGALAMAN BELAJAR ----------
    isi_slot = {"topik": topik.lower(),
                "stimulus": STIMULUS.get(watak, STIMULUS["umum"]),
                "sumber": SUMBER.get(watak, SUMBER["umum"])}

    awal_teks = "\n".join([
        "**Prinsip: berkesadaran, bermakna, menggembirakan** \u2014 alokasi \u00b1 15 menit.",
        "",
        "**Orientasi bermakna:** guru membuka dengan salam dan doa, memeriksa kehadiran "
        "serta kesiapan Murid (kondisi fisik, kelengkapan alat/bahan, dan kesiapan area "
        "kerja), lalu menyampaikan Tujuan Pembelajaran pertemuan ini beserta manfaatnya "
        "dan kriteria keberhasilan yang akan dinilai.",
        "",
        "**Apersepsi kontekstual:** guru menghadirkan %s, kemudian mengajukan pertanyaan "
        "pemantik untuk menghubungkan pengalaman Murid dengan %s."
        % (isi_slot["stimulus"], topik.lower()),
        "",
        "**Motivasi yang menggembirakan:** guru mengaitkan topik dengan peluang nyata "
        "(pekerjaan, karya, atau kegunaan sehari-hari), menyepakati kesepakatan kelas, "
        "dan memastikan setiap Murid merasa aman untuk bertanya dan mencoba.",
        "",
        "**Asesmen awal:** khusus pertemuan pertama, guru melaksanakan asesmen awal "
        "(Lampiran 1) untuk memetakan kesiapan belajar Murid; pada pertemuan berikutnya "
        "bagian ini diganti dengan peninjauan singkat hasil belajar pertemuan sebelumnya.",
    ])

    def sintaks_teks(tahap_kunci):
        out = []
        no = 1
        for nama_tahap, tahap, tmpl in model["sintaks"]:
            if tahap != tahap_kunci:
                continue
            out.append("   %d. **%s:** %s" % (no, nama_tahap, tmpl.format(**isi_slot)))
            no += 1
        return out

    memahami = "\n".join([
        "**MEMAHAMI** \u2014 prinsip: **berkesadaran, bermakna** (\u00b1 %d menit)"
        % max(20, int(meta["jpp"] * meta["menit"] * 0.25)),
        "",
        "*Sasaran pengetahuan:*",
        "   \u2022 **Esensial:** konsep, istilah, dan prinsip dasar %s." % topik.lower(),
        "   \u2022 **Aplikatif:** kegunaan konsep tersebut pada pekerjaan/kegiatan nyata.",
        "   \u2022 **Nilai dan karakter:** kesadaran akan ketertiban, keselamatan, dan "
        "tanggung jawab dalam bekerja.",
        "",
        "*Langkah kegiatan (sintaks %s):*" % model["nama"],
    ] + sintaks_teks("memahami"))

    mengaplikasi = "\n".join([
        "**MENGAPLIKASI** \u2014 prinsip: **bermakna, menggembirakan** (\u00b1 %d menit)"
        % max(30, int(meta["jpp"] * meta["menit"] * 0.45)),
        "",
        "*Sasaran: pendalaman pengetahuan melalui penerapan pada konteks nyata.*",
        "",
        "*Langkah kegiatan (sintaks %s):*" % model["nama"],
    ] + sintaks_teks("mengaplikasi") + [
        "",
        "*Diferensiasi:* kelompok perlu pendampingan mengerjakan tugas dengan panduan "
        "langkah lengkap; kelompok cukup dengan panduan sebagian; kelompok siap diberi "
        "kasus tambahan yang lebih kompleks dan berperan sebagai tutor sebaya.",
        "",
        "*Asesmen proses:* guru mengamati kinerja Murid dengan rubrik pada Lampiran 2 "
        "dan memberi umpan balik saat proses berjalan.",
    ])

    merefleksi = "\n".join([
        "**MEREFLEKSI** \u2014 prinsip: **berkesadaran, bermakna** (\u00b1 %d menit)"
        % max(15, int(meta["jpp"] * meta["menit"] * 0.15)),
        "",
        "*Sasaran: regulasi diri \u2014 Murid menyadari sejauh mana ia telah belajar dan "
        "apa langkah berikutnya.*",
        "",
        "*Langkah kegiatan (sintaks %s):*" % model["nama"],
    ] + sintaks_teks("merefleksi") + [
        "",
        "*Evaluasi diri:* Murid mengisi penilaian diri dan jurnal refleksi (Lampiran 4) "
        "dengan tiga pertanyaan pokok \u2014 apa yang sudah saya kuasai, apa yang masih "
        "sulit, dan apa langkah saya berikutnya.",
    ])

    penutup_teks = "\n".join([
        "**Prinsip: bermakna, berkesadaran** \u2014 alokasi \u00b1 15 menit.",
        "",
        "**Umpan balik konstruktif:** guru menyampaikan hal yang sudah baik dan bagian "
        "yang perlu diperbaiki secara spesifik, bukan sekadar benar/salah, serta memberi "
        "penghargaan atas usaha dan kemajuan Murid.",
        "",
        "**Menyimpulkan bersama:** Murid diminta menyimpulkan pokok pembelajaran dengan "
        "kalimatnya sendiri; guru menegaskan kembali konsep kunci %s." % topik.lower(),
        "",
        "**Rencana belajar berikutnya:** guru menyampaikan topik dan persiapan pertemuan "
        "berikutnya, memberi tugas lanjutan yang sesuai kebutuhan tiap kelompok, dan "
        "menyepakati target perbaikan.",
        "",
        "**Penutup:** merapikan alat dan area kerja bersama-sama, lalu doa dan salam.",
    ])

    asesmen_awal, asesmen_proses, asesmen_akhir = teks_asesmen(watak, tk)

    return {
        "watak": watak, "topik": topik, "identitas": identitas,
        "murid": murid_teks, "materi": materi_teks,
        "dpl": dpl_teks, "dpl_alasan": dpl_alasan, "dpl_list": dpl_dipilih,
        "cp": det.get("cp_teks") or cp, "lintas": ld_teks, "tp": tp_final,
        "tp_items": tp_items, "isi_slot": isi_slot,
        "agenda": agenda, "topik_teks": topik_teks, "pedagogis": ped_teks,
        "kemitraan": kem_teks, "lingkungan": ling_teks, "digital": dig_teks,
        "awal": awal_teks, "memahami": memahami, "mengaplikasi": mengaplikasi,
        "merefleksi": merefleksi, "penutup": penutup_teks,
        "as_awal": asesmen_awal, "as_proses": asesmen_proses, "as_akhir": asesmen_akhir,
        "tk": tk, "model": model, "materi_list": materi_list, "atp_items": atp_items,
        "alokasi_1": ("%d JP / %d pertemuan" % (det["jp"], det["tm"])),
    }


# ============================================================================
# 8. LAMPIRAN ASESMEN
# ============================================================================

STEM_DIAG = [
    "Apa yang sudah kamu ketahui tentang %s?",
    "Pernahkah kamu melihat atau melakukan hal yang berkaitan dengan %s? Ceritakan pengalamanmu.",
    "Menurutmu, mengapa %s penting untuk dikuasai?",
    "Istilah atau alat apa saja yang kamu kenal berkaitan dengan %s?",
    "Bagian apa dari %s yang paling ingin kamu pelajari lebih dalam?",
]


def pertanyaan_diagnostik(isi):
    sumber = isi["materi_list"] or [isi["topik"]]
    hasil = []
    for i in range(5):
        bahan = ringkas(sumber[i % len(sumber)], 70).lower()
        hasil.append(STEM_DIAG[i] % bahan)
    return hasil


def kriteria_rubrik(isi):
    watak = isi["watak"]
    topik = isi["topik"].lower()
    if watak in PRAKTIS:
        return [
            ("Pemahaman konsep",
             "Menjelaskan konsep, istilah, dan prinsip %s dengan benar." % topik,
             ["Menjelaskan seluruh konsep kunci dengan tepat dan mampu memberi contoh sendiri.",
              "Menjelaskan konsep kunci dengan tepat.",
              "Menjelaskan sebagian konsep kunci, masih ada kekeliruan kecil.",
              "Belum dapat menjelaskan konsep kunci tanpa bantuan."]),
            ("Ketepatan prosedur/teknik kerja",
             "Melaksanakan langkah kerja sesuai prosedur dan standar yang berlaku.",
             ["Seluruh langkah dikerjakan urut, tepat, dan mandiri.",
              "Seluruh langkah dikerjakan urut dan tepat dengan sedikit arahan.",
              "Sebagian langkah masih terlewat atau tertukar urutannya.",
              "Langkah kerja belum dapat dilaksanakan tanpa bimbingan penuh."]),
            ("Penerapan K3 dan budaya kerja",
             "Menggunakan alat pelindung diri, bekerja aman, tertib, dan menjaga "
             "kebersihan area kerja.",
             ["Selalu bekerja aman dan tertib, serta mengingatkan teman.",
              "Bekerja aman dan tertib sepanjang kegiatan.",
              "Kadang perlu diingatkan tentang keselamatan/kerapian.",
              "Sering perlu diingatkan dan mengabaikan prosedur keselamatan."]),
            ("Komunikasi dan penyajian hasil",
             "Menyampaikan hasil kerja secara runtut, jujur, dan mudah dipahami.",
             ["Penyajian runtut, jelas, disertai bukti/data, dan menanggapi pertanyaan dengan baik.",
              "Penyajian runtut dan jelas disertai bukti/data.",
              "Penyajian masih kurang runtut atau kurang lengkap.",
              "Belum mampu menyajikan hasil tanpa bantuan."]),
        ]
    return [
        ("Pemahaman konsep",
         "Menjelaskan konsep dan prinsip %s dengan benar." % topik,
         ["Menjelaskan seluruh konsep kunci dengan tepat dan memberi contoh sendiri.",
          "Menjelaskan konsep kunci dengan tepat.",
          "Menjelaskan sebagian konsep kunci, masih ada kekeliruan kecil.",
          "Belum dapat menjelaskan konsep kunci tanpa bantuan."]),
        ("Penalaran kritis",
         "Menganalisis informasi, memberi alasan, dan menarik kesimpulan berbasis bukti.",
         ["Analisis tajam, alasan kuat, kesimpulan didukung bukti lengkap.",
          "Analisis tepat dan kesimpulan didukung bukti.",
          "Analisis masih dangkal, bukti belum lengkap.",
          "Belum menunjukkan analisis dan alasan yang jelas."]),
        ("Kolaborasi dan kemandirian",
         "Bekerja sama secara aktif, menyelesaikan bagian tugasnya, dan tepat waktu.",
         ["Sangat aktif, membantu teman, dan menuntaskan tugas sebelum waktunya.",
          "Aktif bekerja sama dan menuntaskan tugas tepat waktu.",
          "Terlibat sebagian dan tugas selesai terlambat.",
          "Kurang terlibat dan tugas belum selesai."]),
        ("Komunikasi dan penyajian hasil",
         "Menyampaikan hasil secara runtut, santun, dan mudah dipahami.",
         ["Penyajian runtut, jelas, dan menanggapi pertanyaan dengan baik.",
          "Penyajian runtut dan jelas.",
          "Penyajian kurang runtut atau kurang lengkap.",
          "Belum mampu menyajikan hasil tanpa bantuan."]),
    ]


STEM_SOAL = {
    "C1": "Sebutkan %s.",
    "C2": "Jelaskan %s beserta contohnya.",
    "C3": "Uraikan langkah-langkah %s sesuai prosedur/standar yang berlaku.",
    "C4": "Cermati kasus yang diberikan, kemudian analisislah %s dan tentukan tindakan "
          "yang tepat beserta alasannya.",
    "C5": "Nilailah hasil pekerjaan pada contoh yang diberikan berkaitan dengan %s, "
          "lalu kemukakan pertimbanganmu.",
    "C6": "Rancanglah %s dan jelaskan dasar pertimbangan rancanganmu.",
}


def butir_asesmen(isi):
    hasil = []
    for i, tp in enumerate(isi["tp_items"]):
        inti = re.sub(r"^Murid mampu\s+", "", tp, flags=re.I).strip(" .")
        # Buang kata kerja di depan agar tidak bertumpuk dengan kata perintah soal
        # (mis. "Jelaskan menjelaskan ...").
        pokok = re.sub(r"^(meng|meny|mem|men|mer|me|ber)[a-z\-]+\s+", "", inti, flags=re.I)
        if len(pokok) < 12:
            pokok = inti
        lv = level_kognitif(tp)
        stem = STEM_SOAL.get(lv, STEM_SOAL["C2"])
        bentuk = "Uraian" if lv in ("C3", "C4", "C5", "C6") else "Uraian singkat"
        if isi["watak"] in PRAKTIS and lv in ("C3", "C6"):
            bentuk = "Unjuk kerja"
        hasil.append({"no": i + 1, "tp": tp, "inti": inti, "level": lv,
                      "bentuk": bentuk, "soal": stem % ringkas(pokok, 120)})
    return hasil


PERNYATAAN_DIRI = [
    "Saya memahami tujuan pembelajaran pertemuan ini.",
    "Saya dapat menjelaskan konsep pokok materi dengan kalimat saya sendiri.",
    "Saya mengerjakan tugas/praktik sesuai prosedur yang diajarkan.",
    "Saya bertanya ketika ada bagian yang belum saya pahami.",
    "Saya bekerja sama dan menghargai pendapat teman dalam kelompok.",
    "Saya menjaga keselamatan, ketertiban, dan kebersihan selama kegiatan.",
    "Saya jujur dalam mencatat dan melaporkan hasil kerja saya.",
    "Saya menyelesaikan tugas saya tepat waktu.",
    "Saya mengetahui bagian mana yang masih perlu saya latih lagi.",
    "Saya memiliki rencana untuk memperbaiki kekurangan saya.",
]


def tulis_lampiran(doc, isi, meta, id_data, murid):
    diag = pertanyaan_diagnostik(isi)
    krit = kriteria_rubrik(isi)
    butir = butir_asesmen(isi)

    doc.add_page_break()
    paragraf(doc, "LAMPIRAN INSTRUMEN DAN FORM PENILAIAN", 12, True, C, 2)
    paragraf(doc, "%s \u2014 Kelas %s \u2014 %s"
             % (meta["mapel"], id_data["rombel"], isi["topik"]), 9, False, C, 10)

    # ---------------- LAMPIRAN 1 ----------------
    judul_bagian(doc, "LAMPIRAN 1 \u2014 ASESMEN PADA AWAL PEMBELAJARAN")
    paragraf(doc, "Fungsi: *assessment for learning* (diagnostik, tidak diberi nilai "
             "angka). Dilaksanakan pada awal pertemuan pertama, \u00b1 15 menit.",
             9, False, J, 6)
    paragraf(doc, "A. Pertanyaan diagnostik", 10, True, L, 3)
    tabel_data(doc, ["No", "Pertanyaan", "Yang digali"],
               [[str(i + 1), q, k] for i, (q, k) in enumerate(zip(diag, [
                   "Pengetahuan awal", "Pengalaman/konteks", "Kebermaknaan dan minat",
                   "Kosakata/istilah kunci", "Kebutuhan dan minat belajar"]))],
               [1.0, 11.0, 5.0])
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "B. Rubrik sederhana", 10, True, L, 3)
    tabel_data(doc, ["Kategori", "Ketentuan", "Tindak lanjut pembelajaran"],
               [["Siap", "Menjawab tepat pada 4\u20135 pertanyaan",
                 "Diberi pengayaan dan peran tutor sebaya"],
                ["Cukup", "Menjawab tepat pada 2\u20133 pertanyaan",
                 "Mengikuti kegiatan sesuai sasaran dengan bantuan secukupnya"],
                ["Perlu pendampingan", "Menjawab tepat pada 0\u20131 pertanyaan",
                 "Diberi contoh terurai, panduan langkah demi langkah, dan tutor sebaya"]],
               [3.4, 5.6, 8.0])
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "C. Form pemetaan kesiapan belajar Murid", 10, True, L, 3)
    baris = []
    for i, n in enumerate(murid):
        baris.append([str(i + 1), n, "", "", "", "", "", "", "", ""])
    tabel_data(doc, ["No", "Nama Murid", "P1", "P2", "P3", "P4", "P5",
                     "Jml jawaban tepat", "Kategori", "Tindak lanjut"],
               baris, [0.9, 4.0, 0.8, 0.8, 0.8, 0.8, 0.8, 1.6, 2.4, 3.3])
    paragraf(doc, "Petunjuk: beri tanda \u221a pada kolom P1\u2013P5 bila Murid menjawab "
             "tepat, lalu tentukan kategori sesuai rubrik di atas.", 8, False, J, 6)

    # ---------------- LAMPIRAN 2 ----------------
    doc.add_page_break()
    judul_bagian(doc, "LAMPIRAN 2 \u2014 ASESMEN PADA PROSES PEMBELAJARAN")
    paragraf(doc, "Fungsi: *assessment as learning* dan *assessment for learning*. "
             "Teknik: observasi kinerja/sikap selama kegiatan berlangsung disertai "
             "umpan balik saat proses berjalan.", 9, False, J, 6)
    paragraf(doc, "A. Rubrik observasi kinerja (skala 1\u20134)", 10, True, L, 3)
    baris = []
    for i, (nama, indi, lvl) in enumerate(krit):
        baris.append(["K%d" % (i + 1), "**%s**\n%s" % (nama, indi),
                      lvl[0], lvl[1], lvl[2], lvl[3]])
    tabel_data(doc, ["Kode", "Kriteria dan indikator", "4 \u2014 Sangat Baik",
                     "3 \u2014 Baik", "2 \u2014 Cukup", "1 \u2014 Perlu Bimbingan"],
               baris, [1.1, 4.4, 2.9, 2.9, 2.9, 2.8], size=8)
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "B. Lembar observasi kinerja Murid", 10, True, L, 3)
    baris = []
    for i, n in enumerate(murid):
        baris.append([str(i + 1), n, "", "", "", "", "", "", "", ""])
    tabel_data(doc, ["No", "Nama Murid", "K1", "K2", "K3", "K4", "Jml skor",
                     "Nilai", "Predikat", "Umpan balik/tindak lanjut"],
               baris, [0.9, 3.9, 0.9, 0.9, 0.9, 0.9, 1.4, 1.2, 1.8, 4.2])
    paragraf(doc, "Nilai = (jumlah skor \u00f7 16) \u00d7 100. Predikat: 90\u2013100 "
             "Sangat Baik; 80\u201389 Baik; 75\u201379 Cukup; < 75 Perlu Bimbingan.",
             8, False, J, 6)

    # ---------------- LAMPIRAN 3 ----------------
    doc.add_page_break()
    judul_bagian(doc, "LAMPIRAN 3 \u2014 ASESMEN PADA AKHIR PEMBELAJARAN")
    paragraf(doc, "Fungsi: *assessment of learning*. Bentuk: %s (%s)."
             % (isi["tk"]["akhir_nama"], isi["tk"]["bentuk"]), 9, False, J, 6)
    paragraf(doc, "A. Kisi-kisi asesmen", 10, True, L, 3)
    tabel_data(doc, ["No", "Tujuan Pembelajaran", "Indikator asesmen",
                     "Level", "Bentuk", "No. butir"],
               [[str(b["no"]), b["tp"],
                 "Murid dapat %s sesuai kriteria yang ditetapkan." % ringkas(b["inti"], 120),
                 b["level"], b["bentuk"], str(b["no"])] for b in butir],
               [0.9, 5.0, 5.4, 1.2, 2.1, 1.4], size=8)
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "B. Butir asesmen", 10, True, L, 3)
    tabel_data(doc, ["No", "Butir asesmen", "Bentuk", "Skor maks"],
               [[str(b["no"]), b["soal"], b["bentuk"], "4"] for b in butir],
               [0.9, 11.6, 2.3, 2.2], size=8.5)
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "C. Pedoman penskoran", 10, True, L, 3)
    tabel_data(doc, ["Skor", "Ketentuan"],
               [["4", "Jawaban/hasil kerja lengkap, tepat, runtut, dan disertai alasan "
                      "atau bukti yang benar."],
                ["3", "Jawaban/hasil kerja tepat, tetapi kurang lengkap atau alasan belum kuat."],
                ["2", "Jawaban/hasil kerja tepat sebagian, masih terdapat kekeliruan."],
                ["1", "Jawaban/hasil kerja belum tepat atau belum dikerjakan."]],
               [1.6, 15.4])
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "D. Daftar nilai asesmen akhir", 10, True, L, 3)
    hdr = ["No", "Nama Murid"] + ["B%d" % b["no"] for b in butir] + \
          ["Jml", "Nilai", "Predikat", "Ket. (T/BT)"]
    # Lebar kolom butir menyesuaikan banyaknya butir asesmen supaya tabel tetap
    # muat pada satu halaman A4 potret.
    tetap = [0.9, 3.9, 1.1, 1.2, 1.8, 1.9]
    tersedia = LEBAR_HALAMAN - sum(tetap)
    n_b = max(1, len(butir))
    wb = tersedia / n_b
    if wb > 0.85:
        wb = 0.85
    if wb < LEBAR_MINIMUM:
        wb = LEBAR_MINIMUM
    w = [0.9, 3.9] + [wb] * len(butir) + [1.1, 1.2, 1.8, 1.9]
    sisa = LEBAR_HALAMAN - sum(w)
    if sisa > 0:
        w[1] += sisa
    baris = []
    for i, n in enumerate(murid):
        baris.append([str(i + 1), n] + [""] * len(butir) + ["", "", "", ""])
    tabel_data(doc, hdr, baris, w, size=8)
    paragraf(doc, "Nilai = (jumlah skor \u00f7 (%d \u00d7 4)) \u00d7 100. "
             "T = Tuntas (\u2265 75); BT = Belum Tuntas (< 75). "
             "Predikat: 90\u2013100 Sangat Baik; 80\u201389 Baik; 75\u201379 Cukup; "
             "< 75 Perlu Bimbingan." % len(butir), 8, False, J, 6)
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "E. Rencana tindak lanjut", 10, True, L, 3)
    tabel_data(doc, ["Hasil", "Tindak lanjut"],
               [["Nilai < 75 (Belum Tuntas)",
                 "Pembelajaran remedial terbimbing pada indikator yang belum tuntas, "
                 "kemudian asesmen ulang pada butir yang bersangkutan."],
                ["Nilai 75\u201389",
                 "Penguatan melalui latihan tambahan pada bagian yang masih lemah."],
                ["Nilai \u2265 90",
                 "Pengayaan berupa tugas/kasus dengan tingkat kesulitan lebih tinggi "
                 "dan peran sebagai tutor sebaya."]],
               [4.2, 12.8])

    # ---------------- LAMPIRAN 4 ----------------
    doc.add_page_break()
    judul_bagian(doc, "LAMPIRAN 4 \u2014 PENILAIAN DIRI, PENILAIAN ANTARTEMAN, "
                      "DAN JURNAL REFLEKSI")
    paragraf(doc, "Fungsi: *assessment as learning* \u2014 melatih regulasi diri Murid. "
             "Diisi Murid pada tahap Merefleksi.", 9, False, J, 6)
    paragraf(doc, "A. Lembar penilaian diri (diisi masing-masing Murid)", 10, True, L, 3)
    tabel_data(doc, ["No", "Pernyataan", "Selalu", "Sering", "Jarang", "Belum"],
               [[str(i + 1), p, "", "", "", ""] for i, p in enumerate(PERNYATAAN_DIRI)],
               [0.9, 9.7, 1.6, 1.6, 1.6, 1.6])
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "B. Rekap penilaian diri dan penilaian antarteman", 10, True, L, 3)
    baris = []
    for i, n in enumerate(murid):
        baris.append([str(i + 1), n, "", "", "", ""])
    tabel_data(doc, ["No", "Nama Murid", "Skor penilaian diri",
                     "Skor penilaian antarteman", "Rata-rata", "Catatan guru"],
               baris, [0.9, 4.2, 2.7, 3.0, 1.8, 4.4])
    paragraf(doc, "Skala: Selalu = 4; Sering = 3; Jarang = 2; Belum = 1. "
             "Skor = (jumlah \u00f7 40) \u00d7 100. Hasil dipakai sebagai bahan "
             "pembinaan, bukan penentu nilai rapor.", 8, False, J, 6)
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "C. Jurnal refleksi Murid", 10, True, L, 3)
    tabel_data(doc, ["No", "Nama Murid", "Yang sudah saya kuasai",
                     "Yang masih sulit bagi saya", "Rencana saya berikutnya"],
               [[str(i + 1), n, "", "", ""] for i, n in enumerate(murid)],
               [0.9, 3.9, 4.1, 4.1, 4.0])
    paragraf(doc, "", 8, False, L, 4)
    paragraf(doc, "D. Refleksi guru", 10, True, L, 3)
    tabel_data(doc, ["Pertanyaan refleksi", "Catatan"],
               [["Bagian mana dari pembelajaran ini yang paling membantu Murid memahami "
                 "materi?", ""],
                ["Kesulitan apa yang paling banyak dialami Murid, dan apa dugaan "
                 "penyebabnya?", ""],
                ["Apakah alokasi waktu tiap tahap sudah memadai? Bagian mana yang perlu "
                 "ditambah/dikurangi?", ""],
                ["Apa yang akan saya perbaiki pada pertemuan berikutnya?", ""]],
               [8.0, 9.0])

    paragraf(doc, "", 9, False, L, 10)
    # Blok tanda tangan bersama (sama bentuknya dengan ATP dan KKTP): Kepala
    # Sekolah sudah tetap, Guru Mata Pelajaran diambil dari pengisian form.
    blok_ttd(doc, id_data["kota"], id_data.get("penyusun"), id_data.get("npa"),
             id_data["instansi"], lebar=(8.5, 8.5))


# ============================================================================
# 9. PENULIS DOKUMEN RPM
# ============================================================================


def fmt_minggu(x):
    """Tampilkan jumlah minggu efektif secara wajar (bulat atau satu desimal)."""
    if abs(x - round(x)) < 0.05:
        return "%d" % int(round(x))
    return ("%.1f" % x).replace(".", ",")


def pokok_tp(tp):
    """Ambil pokok materi dari sebuah rumusan Tujuan Pembelajaran."""
    x = re.sub(r"^\s*\d+\.\s*", "", tp.strip())
    x = re.sub(r"^Murid mampu\s+", "", x, flags=re.I)
    x = re.sub(r"^(meng|meny|mem|men|mer|me|ber)[a-z\-]+\s+", "", x, flags=re.I)
    return ringkas(x, 110)


def bagi_menit(jpp, menit):
    """Bagi menit satu pertemuan ke tahap awal, inti, dan penutup."""
    total = jpp * menit
    tepi = 15 if total >= 120 else max(5, int(round(total * 0.12)))
    inti = total - 2 * tepi
    paham = int(round(inti * 0.30))
    apl = int(round(inti * 0.50))
    ref = inti - paham - apl
    return {"total": total, "awal": tepi, "penutup": tepi, "inti": inti,
            "paham": paham, "apl": apl, "ref": ref}


def blok_pengalaman(isi, meta, det):
    """Uraikan PENGALAMAN BELAJAR untuk SETIAP pertemuan pada unit RPM ini.

    Setiap pertemuan memperoleh AWAL\u2013INTI\u2013PENUTUP tersendiri dengan Tujuan
    Pembelajaran, fokus materi, dan penanda asesmen yang berbeda, sehingga
    jumlah uraian pertemuan selalu sama dengan alokasi waktu pada ATP.
    """
    model = isi["model"]
    slot = isi["isi_slot"]
    topik = isi["topik"]
    pert = det.get("pertemuan") or []
    n = len(pert)
    mnt = bagi_menit(meta["jpp"], meta["menit"])

    def sintaks(tahap_kunci, fokus):
        s2 = dict(slot)
        s2["topik"] = fokus
        out = []
        no = 1
        for nama_tahap, tahap, tmpl in model["sintaks"]:
            if tahap != tahap_kunci:
                continue
            out.append("   %d. **%s:** %s" % (no, nama_tahap, tmpl.format(**s2)))
            no += 1
        return out

    hasil = []
    for i, (lab, items, _n_pert) in enumerate(pert):
        pertama = (i == 0)
        terakhir = (i == n - 1)
        fokus_list = [pokok_tp(x) for x in items]
        fokus = fokus_list[0] if len(fokus_list) == 1 else "; ".join(fokus_list)
        tp_baris = ["   %s" % (x if x.endswith(".") else x + ".") for x in items]

        # ---------------- AWAL ----------------
        awal = [
            "**Prinsip: berkesadaran, bermakna, menggembirakan** \u2014 \u00b1 %d menit."
            % mnt["awal"],
            "",
            "**Tujuan Pembelajaran pertemuan ini:**",
        ] + tp_baris + [
            "",
            "**Orientasi:** guru membuka dengan salam dan doa, memeriksa kehadiran serta "
            "kesiapan Murid (kondisi fisik, kelengkapan alat/bahan, dan kesiapan area "
            "kerja), lalu menyampaikan Tujuan Pembelajaran di atas beserta manfaatnya dan "
            "kriteria keberhasilan yang akan dinilai.",
            "",
        ]
        if pertama:
            awal += [
                "**Apersepsi:** guru menghadirkan %s, kemudian mengajukan pertanyaan "
                "pemantik untuk menghubungkan pengalaman Murid dengan %s."
                % (slot["stimulus"], topik.lower()),
                "",
                "**Asesmen awal:** guru melaksanakan asesmen awal (Lampiran 1) untuk "
                "memetakan kesiapan belajar Murid, lalu membagi Murid menjadi kelompok "
                "perlu pendampingan, cukup, dan siap/melampaui.",
                "",
                "**Kesepakatan kelas:** guru dan Murid menyepakati aturan kerja, "
                "keselamatan, dan cara saling menghargai selama pembelajaran.",
            ]
        else:
            awal += [
                "**Peninjauan pertemuan sebelumnya:** guru mengajak Murid mengingat "
                "kembali hasil belajar pertemuan ke-%d, membahas kekeliruan yang masih "
                "muncul, lalu mengaitkannya dengan %s." % (i, fokus),
                "",
                "**Apersepsi:** guru menghadirkan %s sebagai pijakan menuju bahasan "
                "pertemuan ini." % slot["stimulus"],
                "",
                "**Motivasi:** guru menegaskan kegunaan nyata %s bagi pekerjaan dan "
                "kehidupan sehari-hari Murid." % fokus,
            ]

        # ---------------- INTI ----------------
        inti = [
            "**MEMAHAMI** \u2014 prinsip: **berkesadaran, bermakna** (\u00b1 %d menit)"
            % mnt["paham"],
            "",
            "*Fokus materi pertemuan ini: %s.*" % fokus,
            "",
            "*Sasaran pengetahuan:*",
            "   \u2022 **Esensial:** konsep, istilah, dan prinsip dasar pada %s." % fokus,
            "   \u2022 **Aplikatif:** kegunaan konsep tersebut pada pekerjaan nyata.",
            "   \u2022 **Nilai dan karakter:** ketertiban, keselamatan, kejujuran, dan "
            "tanggung jawab dalam bekerja.",
            "",
            "*Langkah kegiatan (sintaks %s):*" % model["nama"],
        ] + sintaks("memahami", fokus) + [
            "",
            "**MENGAPLIKASI** \u2014 prinsip: **bermakna, menggembirakan** (\u00b1 %d menit)"
            % mnt["apl"],
            "",
            "*Sasaran: pendalaman pengetahuan melalui penerapan %s pada konteks nyata.*"
            % fokus,
            "",
            "*Langkah kegiatan (sintaks %s):*" % model["nama"],
        ] + sintaks("mengaplikasi", fokus) + [
            "",
            "*Diferensiasi:* kelompok perlu pendampingan bekerja dengan panduan langkah "
            "lengkap dan pendampingan tutor sebaya; kelompok cukup dengan panduan "
            "sebagian; kelompok siap diberi kasus tambahan yang lebih kompleks.",
            "",
            "*Asesmen proses:* guru mengamati kinerja Murid memakai rubrik pada Lampiran 2 "
            "dan memberi umpan balik saat proses berjalan.",
            "",
            "**MEREFLEKSI** \u2014 prinsip: **berkesadaran, bermakna** (\u00b1 %d menit)"
            % mnt["ref"],
            "",
            "*Sasaran: regulasi diri \u2014 Murid menyadari sejauh mana ia telah menguasai "
            "%s dan apa langkah berikutnya.*" % fokus,
            "",
            "*Langkah kegiatan (sintaks %s):*" % model["nama"],
        ] + sintaks("merefleksi", fokus) + [
            "",
            "*Evaluasi diri:* Murid mengisi penilaian diri dan jurnal refleksi (Lampiran 4) "
            "dengan tiga pertanyaan pokok \u2014 apa yang sudah saya kuasai, apa yang "
            "masih sulit, dan apa langkah saya berikutnya.",
        ]

        # ---------------- PENUTUP ----------------
        penutup = [
            "**Prinsip: bermakna, berkesadaran** \u2014 \u00b1 %d menit." % mnt["penutup"],
            "",
            "**Umpan balik:** guru menyampaikan hal yang sudah baik dan bagian yang perlu "
            "diperbaiki secara spesifik, serta memberi penghargaan atas usaha Murid.",
            "",
            "**Menyimpulkan bersama:** Murid menyimpulkan pokok pembelajaran dengan "
            "kalimatnya sendiri; guru menegaskan kembali konsep kunci pada %s." % fokus,
            "",
        ]
        if terakhir:
            penutup += [
                "**Asesmen akhir:** guru melaksanakan asesmen akhir (Lampiran 3) untuk "
                "mengukur ketercapaian seluruh Tujuan Pembelajaran pada unit ini, "
                "kemudian menyampaikan rencana remedial bagi yang belum mencapai 75 dan "
                "pengayaan bagi yang telah melampaui.",
                "",
                "**Penutup:** merapikan alat dan area kerja bersama-sama, lalu doa dan salam.",
            ]
        else:
            penutup += [
                "**Rencana pertemuan berikutnya:** guru menyampaikan bahasan pertemuan "
                "ke-%d beserta persiapan yang perlu dibawa Murid, dan memberi tugas "
                "lanjutan sesuai kebutuhan tiap kelompok." % (i + 2),
                "",
                "**Penutup:** merapikan alat dan area kerja bersama-sama, lalu doa dan salam.",
            ]

        hasil.append({
            "judul": "%s dari %d  \u2014  %d JP \u00d7 %d menit = %d menit"
                     % (lab, n, meta["jpp"], meta["menit"], mnt["total"]),
            "awal": "\n".join(awal),
            "inti": "\n".join(inti),
            "penutup": "\n".join(penutup),
            "menit": mnt,
        })
    return hasil


def tulis_rpm(det, meta, s, id_data, murid, urut_prev, out_path):
    isi = susun_isi(det, meta, s, id_data, urut_prev)

    doc = Document()
    sec = doc.sections[0]
    sec.page_width = Cm(21.0)
    sec.page_height = Cm(29.7)
    sec.left_margin = Cm(2.0)
    sec.right_margin = Cm(2.0)
    sec.top_margin = Cm(2.0)
    sec.bottom_margin = Cm(2.0)

    st = doc.styles["Normal"]
    st.font.name = FONT
    st.font.size = Pt(10)

    paragraf(doc, "RANCANGAN PEMBELAJARAN MENDALAM (RPM)", 13, True, C, 1)
    paragraf(doc, "%s \u2014 Tahun Pelajaran %s" % (id_data["instansi"], meta["tahun"]),
             10, False, C, 1)
    if det.get("nomor_tp"):
        no = det["nomor_tp"]
        cak = ("TP ke-%d" % no[0]) if len(no) == 1 else \
            ("TP ke-%s" % ", ".join(str(x) for x in no))
        kepala = "%s \u2014 %s (%d pertemuan) \u2014 %s" \
            % (det["label_semester"], cak, det["tm"], meta["mapel"])
    else:
        kepala = "%s \u2014 Elemen ke-%d dari %s" \
            % (det["label_semester"], det["urut"], meta["mapel"])
    paragraf(doc, kepala, 9, False, C, 8)

    # ---------- TABEL IDENTITAS ----------
    judul_bagian(doc, "IDENTITAS")
    tabel_label(doc, isi["identitas"], w=(4.6, 12.4), size=10, warna="D9E2F3")

    # ---------- A. IDENTIFIKASI ----------
    judul_bagian(doc, "A. IDENTIFIKASI")
    tabel_label(doc, [
        ("Murid", isi["murid"]),
        ("Materi Pelajaran", isi["materi"]),
        ("Dimensi Profil Lulusan", isi["dpl"] + "\n\n" + isi["dpl_alasan"]),
    ])

    # ---------- B. DESAIN PEMBELAJARAN ----------
    judul_bagian(doc, "B. DESAIN PEMBELAJARAN")
    tabel_label(doc, [
        ("Elemen", det.get("elemen_teks") or det["elemen"]),
        ("Capaian Pembelajaran", isi["cp"] + "\n\n*(Dikutip apa adanya dari Lampiran %s "
         "butir %s %s.)*" % (s["lampiran"], s["kode"], RUJUKAN_CP)),
        ("Lintas Disiplin Ilmu", isi["lintas"]),
        ("Tujuan Pembelajaran", isi["tp"] +
         "\n\n*(%d Tujuan Pembelajaran disebar ke %d pertemuan sesuai alokasi waktu "
         "pada ATP; uraian kegiatan tiap pertemuan ada pada bagian C.)*"
         % (len(isi["tp_items"]), det["tm"])),
        ("Topik Pembelajaran", isi["topik_teks"]),
        ("Praktik Pedagogis\n*(Model/Strategi/Metode)*", isi["pedagogis"]),
        ("Kemitraan Pembelajaran", isi["kemitraan"]),
        ("Lingkungan Pembelajaran", isi["lingkungan"]),
        ("Pemanfaatan Digital", isi["digital"]),
    ])

    # ---------- C. PENGALAMAN BELAJAR ----------
    judul_bagian(doc, "C. PENGALAMAN BELAJAR")
    blok = blok_pengalaman(isi, meta, det)
    paragraf(doc, "Diuraikan untuk seluruh %d pertemuan pada unit RPM ini "
             "(%d JP \u00d7 %d menit = %d menit per pertemuan), sesuai alokasi waktu "
             "pada ATP. Setiap pertemuan memiliki Tujuan Pembelajaran dan fokus materi "
             "tersendiri."
             % (len(blok), meta["jpp"], meta["menit"], meta["jpp"] * meta["menit"]),
             9, False, J, 6)
    for b in blok:
        paragraf(doc, b["judul"], 10, True, L, 2)
        tabel_label(doc, [
            ("AWAL\n*(\u00b1 %d menit)*" % b["menit"]["awal"], b["awal"]),
            ("INTI\n*(\u00b1 %d menit)*" % b["menit"]["inti"], b["inti"]),
            ("PENUTUP\n*(\u00b1 %d menit)*" % b["menit"]["penutup"], b["penutup"]),
        ])
        paragraf(doc, "", 8, False, L, 6)

    # ---------- D. ASESMEN PEMBELAJARAN ----------
    judul_bagian(doc, "D. ASESMEN PEMBELAJARAN")
    tabel_label(doc, [
        ("Asesmen pada Awal Pembelajaran", isi["as_awal"]),
        ("Asesmen pada Proses Pembelajaran", isi["as_proses"]),
        ("Asesmen pada Akhir Pembelajaran", isi["as_akhir"]),
    ])

    # ---------- CATATAN SUMBER ----------
    paragraf(doc, "", 9, False, L, 6)
    t = doc.add_table(rows=1, cols=1)
    t.style = "Table Grid"
    t.alignment = WD_TABLE_ALIGNMENT.CENTER
    sel(t.cell(0, 0),
        "**Catatan sumber (dapat diperiksa):** Elemen dan Capaian Pembelajaran dikutip "
        "apa adanya dari Lampiran %s butir %s %s. Alokasi waktu pada RPM ini (%d JP = "
        "%d pertemuan \u00d7 %d JP, setara %s minggu efektif pada %d JP/minggu) diambil "
        "langsung dari Alur Tujuan Pembelajaran (ATP) mata pelajaran yang sama, sehingga "
        "ATP dan RPM sinkron. Kerangka isi mengikuti format templat resmi Rancangan "
        "Pembelajaran Mendalam: prinsip berkesadaran\u2013bermakna\u2013menggembirakan, "
        "pengalaman belajar memahami\u2013mengaplikasi\u2013merefleksi, dan 8 Dimensi "
        "Profil Lulusan."
        % (s["lampiran"], s["kode"], RUJUKAN_CP, det["jp"], det["tm"], meta["jpp"],
           fmt_minggu(det["minggu"]), meta["jp"]),
        J, 8)
    lebar(t, [17.0])

    # ---------- LAMPIRAN ----------
    tulis_lampiran(doc, isi, meta, id_data, murid)

    doc.save(out_path)
    return out_path


# ============================================================================
# 10. GENERATOR (PILIHAN LINGKUP)
# ============================================================================


def kode_unit(nomor_tp):
    """Penanda ringkas cakupan TP untuk nama berkas, mis. "TP 1-3" atau "TP 1,2,4"."""
    if not nomor_tp:
        return ""
    if len(nomor_tp) == 1:
        return "TP %d" % nomor_tp[0]
    urut = sorted(nomor_tp)
    if urut == list(range(urut[0], urut[-1] + 1)):
        return "TP %d-%d" % (urut[0], urut[-1])
    return "TP " + ",".join(str(x) for x in urut)


def nama_file(meta, det, urut_berkas=None):
    m = re.sub(r"[^A-Za-z0-9 ]", "", meta["mapel"]).strip().upper()
    e = re.sub(r"[^A-Za-z0-9 ]", "", topik_utama(det["elemen"])).strip().upper()
    no = urut_berkas if urut_berkas is not None else det["urut"]
    if det.get("topik"):
        tk = re.sub(r"[^A-Za-z0-9 ]", "", det["topik"]).strip().upper()
        return ("RPM %02d - %s - KELAS %s SEM %d - %s.docx"
                % (no, m, meta["kelas"], det["semester"], tk[:60].strip()))
    kode = kode_unit(det.get("nomor_tp"))
    if kode:
        return ("RPM %02d - %s - KELAS %s SEM %d - %s - %s.docx"
                % (no, m, meta["kelas"], det["semester"], kode, e[:40].strip()))
    return ("RPM %02d - %s - KELAS %s SEM %d - %s.docx"
            % (no, m, meta["kelas"], det["semester"], e[:55].strip()))


def baca_murid(path):
    if not path:
        return list(MURID_CONTOH)
    if not os.path.isfile(path):
        print("\u26a0\ufe0f  Berkas daftar murid tidak ditemukan: %s" % path)
        print("   Dipakai 10 nama contoh.")
        return list(MURID_CONTOH)
    nama = []
    for ln in io.open(path, encoding="utf-8").read().split("\n"):
        ln = re.sub(r"^\s*\d+[\.\)]\s*", "", ln).strip()
        if ln:
            nama.append(ln)
    return nama[:45] if nama else list(MURID_CONTOH)


def detail_aktif(r, semester=None):
    """Ambil detail elemen dari semester aktif; bila `semester` diisi, disaring."""
    semua = []
    for b in r["blok"]:
        if b["aktif"]:
            semua.extend(b["detail"])
    if semester in (None, ""):
        return semua
    key = str(semester).lower().strip()
    return [d for d in semua if d["jenis"] == key or str(d["semester"]) == key]


def generate_rpm(mapel, jp, kelas=None, fase=None, mg=22, mn=15, menit=45, jpp=None,
                 lingkup="tahun", semester=None, elemen=None, tp=None, tp_per_tm=1,
                 topik_n=None,
                 instansi="SMKS PGRI 2 Jambi", npsn="...........................",
                 penyusun="...........................", npa=None, rombel=None,
                 kota="Jambi", tahun="2026/2027", murid_file=None,
                 elemen_kelas=None, out_dir=None, diam=False):
    """Membuat berkas RPM.

    Lingkup yang tersedia:
      tahun    \u2014 seluruh elemen kelas ini (2 semester), 1 DOCX per elemen,
                  seluruh pertemuannya diuraikan;
      semester \u2014 satu semester saja, 1 DOCX per elemen;
      elemen   \u2014 satu Elemen/CP saja, seluruh pertemuannya diuraikan;
      tp       \u2014 satu kelompok Tujuan Pembelajaran pilihan = 1 DOCX.
    """
    r = susun_rencana(mapel, jp, kelas=kelas, fase=fase, mg=mg, mn=mn, menit=menit,
                      jpp=jpp, sekolah=instansi, tahun=tahun, elemen_kelas=elemen_kelas)
    meta = r["meta"]
    s = r["s"]
    semua = detail_aktif(r)

    lingkup = (lingkup or "tahun").lower().strip()
    unit_list = []

    # ------------- lingkup berbasis Materi Pokok (1 topik = 1 berkas) -------------
    if lingkup in ("topik", "materi", "materipokok", "materi-pokok", "topikmateri"):
        sumber = detail_aktif(r, semester)
        if not sumber:
            print("\u274c Semester '%s' tidak tersedia untuk Kelas %s."
                  % (semester, meta["kelas"]))
            sys.exit(1)
        if elemen not in (None, "", 0, "0", "semua"):
            n_el = int(elemen)
            saring = [d for d in sumber if d["urut_kelas"] == n_el]
            if not saring:
                print("\u274c Elemen/CP nomor %d tidak ada pada semester tersebut." % n_el)
                print("   Pilihan yang ada:")
                for d in sumber:
                    print("     %d. %s" % (d["urut_kelas"], topik_utama(d["elemen"])))
                sys.exit(1)
            sumber = saring
        for d in sumber:
            for g in bagi_topik(daftar_tp([d]), d["tm"], topik_n, det=d):
                unit = bangun_unit(g["butir"], meta, n_tm=g["tm"],
                                   nomor_unit=len(unit_list) + 1, topik=g["nama"])
                unit["nomor_tp"] = None
                unit["urut_kelas"] = d["urut_kelas"]
                unit_list.append(unit)
        ket_lingkup = ("1 Materi Pokok per berkas \u2014 %d Elemen/CP \u2192 %d Materi Pokok"
                       % (len(sumber), len(unit_list)))
    # ---------------- lingkup berbasis Tujuan Pembelajaran ----------------
    elif lingkup in ("tp", "tujuan", "kelompoktp", "kelompok-tp"):
        sumber = detail_aktif(r, semester)
        if not sumber:
            print("\u274c Semester '%s' tidak tersedia untuk Kelas %s." % (semester, meta["kelas"]))
            sys.exit(1)
        ket_el = ""
        if elemen not in (None, "", 0, "0", "semua"):
            n_el = int(elemen)
            saring = [d for d in sumber if d["urut_kelas"] == n_el]
            if not saring:
                print("\u274c Elemen/CP nomor %d tidak ada pada semester tersebut." % n_el)
                print("   Pilihan yang ada:")
                for d in sumber:
                    print("     %d. %s" % (d["urut_kelas"], topik_utama(d["elemen"])))
                sys.exit(1)
            sumber = saring
            ket_el = (" dalam Elemen/CP ke-%d (%s)"
                      % (n_el, topik_utama(sumber[0]["elemen"])))
        entri = daftar_tp(sumber)
        nomor, galat = urai_pilihan(tp if tp is not None else "semua", len(entri))
        if nomor is None:
            print("\u274c %s" % galat)
            sys.exit(1)
        dipilih = [e for e in entri if e["no"] in nomor]
        unit = bangun_unit(dipilih, meta, tp_per_tm=tp_per_tm, nomor_unit=1)
        unit_list = [unit]
        ket_lingkup = ("kelompok Tujuan Pembelajaran \u2014 %s (%d TP \u2192 %d pertemuan, "
                       "%d TP per pertemuan)"
                       % (kode_unit(unit["nomor_tp"]), len(dipilih), unit["tm"],
                          tp_per_tm)) + ket_el
    else:
        # ------------- lingkup berbasis elemen (semua pertemuan diuraikan) -------------
        if lingkup in ("tahun", "2semester", "2", "setahun"):
            pilih = semua
            ket_lingkup = "2 semester (seluruh elemen Kelas %s)" % meta["kelas"]
        elif lingkup in ("semester", "1semester", "1"):
            key = semester if semester is not None else "ganjil"
            pilih = detail_aktif(r, key)
            if not pilih:
                print("\u274c Semester '%s' tidak tersedia untuk Kelas %s." % (key, meta["kelas"]))
                print("   Yang tersedia: %s"
                      % ", ".join("%d (%s)" % (b["no"], b["jenis"])
                                  for b in r["blok"] if b["aktif"]))
                sys.exit(1)
            ket_lingkup = "1 semester \u2014 %s" % pilih[0]["label_semester"]
        elif lingkup in ("elemen", "cp", "1elemen"):
            n = int(elemen or 1)
            pilih = [d for d in semua if d["urut_kelas"] == n]
            if not pilih:
                print("\u274c Elemen nomor %d tidak ada pada Kelas %s." % (n, meta["kelas"]))
                print("   Pilihan yang ada:")
                for d in semua:
                    print("     %d. %s  [%s]" % (d["urut_kelas"], topik_utama(d["elemen"]),
                                                 d["label_semester"]))
                sys.exit(1)
            ket_lingkup = "1 Elemen/CP \u2014 elemen ke-%d" % n
        else:
            print("\u274c Lingkup harus: tahun / semester / elemen / tp / topik.")
            sys.exit(1)

        for d in pilih:
            entri = daftar_tp([d])
            # jumlah pertemuan dikunci pada angka ATP supaya tetap sinkron
            unit = bangun_unit(entri, meta, n_tm=d["tm"], nomor_unit=d["urut"])
            unit["nomor_tp"] = None
            unit["urut_kelas"] = d["urut_kelas"]
            unit["minggu"] = d["minggu"]
            unit["jp"] = d["jp"]
            unit_list.append(unit)

    murid = baca_murid(murid_file)
    id_data = {"instansi": instansi, "npsn": npsn, "penyusun": penyusun,
               "npa": npa, "rombel": rombel or meta["kelas"], "kota": kota,
               "kepala": KEPALA_NAMA, "kepala_npa": KEPALA_NPA}

    if out_dir is None:
        m = re.sub(r"[^A-Za-z0-9 ]", "", meta["mapel"]).strip().upper()
        out_dir = os.path.join(BASE_OUT, "RPM %s - KELAS %s FASE %s"
                               % (m, meta["kelas"], meta["fase"]))
    out_dir = os.path.abspath(out_dir)
    if not os.path.isdir(out_dir):
        os.makedirs(out_dir)

    hasil = []
    for i, u in enumerate(unit_list):
        prev = [topik_utama(x["elemen"]) for x in semua
                if x["urut_kelas"] < u.get("urut_kelas", 99)]
        p = os.path.join(out_dir, nama_file(meta, u, urut_berkas=i + 1))
        tulis_rpm(u, meta, s, id_data, murid, prev, p)
        hasil.append((u, p))

    if not diam:
        total_tm = sum(u["tm"] for u, _ in hasil)
        print("\u2705 BERHASIL \u2014 %d berkas RPM dibuat (%d uraian pertemuan)"
              % (len(hasil), total_tm))
        print("   Mapel        : %s  (Lampiran %s, %s)" % (meta["mapel"], s["lampiran"], s["kode"]))
        print("   Fase / Kelas : %s / %s" % (meta["fase"], meta["kelas"]))
        print("   Lingkup      : %s" % ket_lingkup)
        print("   Pola waktu   : %d JP/minggu = %d pertemuan x %d JP (%d menit/pertemuan)"
              % (meta["jp"], meta["tm_minggu"], meta["jpp"], meta["jpp"] * meta["menit"]))
        print("   Minggu       : %s" % meta["info_minggu"])
        print("   Daftar murid : %d nama%s"
              % (len(murid), " (contoh bawaan)" if not murid_file else ""))
        print("")
        for u, p in hasil:
            print("   \u2022 [%s] %s" % (u["label_semester"], topik_utama(u["elemen"])))
            print("       %d JP | %d pertemuan (diuraikan semua) | %d TP"
                  % (u["jp"], u["tm"], len(u["tp_list"])))
            print("       %s" % os.path.basename(p))
        for w in r["peringatan"]:
            print("   \u26a0\ufe0f  %s" % w)
        print("")
        print("   \U0001f4c1 TERSIMPAN DI FOLDER:")
        print("   %s" % out_dir)
    return [p for _, p in hasil]


# ============================================================================
# 11. MODE FORM INTERAKTIF
# ============================================================================


def tanya(label, bawaan=None, wajib=False):
    tambah = "" if bawaan in (None, "") else " [%s]" % bawaan
    while True:
        try:
            v = raw_input("%s%s: " % (label, tambah))  # noqa: F821
        except NameError:
            v = input("%s%s: " % (label, tambah))
        v = v.strip()
        if not v and bawaan is not None:
            return bawaan
        if v:
            return v
        if not wajib:
            return ""
        print("   (wajib diisi)")


def tanya_angka(label, bawaan=None, minimal=1):
    while True:
        v = tanya(label, str(bawaan) if bawaan is not None else None, wajib=True)
        try:
            n = int(str(v).strip())
        except ValueError:
            print("   (harus berupa angka)")
            continue
        if n < minimal:
            print("   (minimal %d)" % minimal)
            continue
        return n


def mode_form():
    db = load_db()
    print("=" * 74)
    print("  GENERATOR RPM \u2014 RANCANGAN PEMBELAJARAN MENDALAM")
    print("  Kurikulum Nasional 2025 \u2014 SMK/MAK")
    print("=" * 74)
    print("  Elemen, Capaian Pembelajaran, dan alokasi waktu diambil dari mesin ATP,")
    print("  sehingga ATP dan RPM selalu sinkron.")
    print("-" * 74)

    # [1] mapel
    while True:
        mapel = tanya("[1] Mata Pelajaran", wajib=True)
        kand = cari_mapel(db, mapel)
        if not kand:
            print("    \u274c Tidak ditemukan. Coba kata kunci lain.")
            continue
        if len(kand) == 1:
            pilih = kand[0]
        else:
            print("    Ditemukan beberapa pilihan:")
            for i, s in enumerate(kand[:10]):
                print("      %d. %s  [Lampiran %s %s, fase: %s]"
                      % (i + 1, s["nama_asli"].title(), s["lampiran"], s["kode"],
                         ", ".join(sorted(s["fase"]))))
            n = tanya_angka("    Pilih nomor", 1)
            if n > len(kand[:10]):
                continue
            pilih = kand[n - 1]
        print("    \u2713 %s  [Lampiran %s %s, fase tersedia: %s]"
              % (pilih["nama_asli"].title(), pilih["lampiran"], pilih["kode"],
                 ", ".join(sorted(pilih["fase"]))))
        mapel = pilih["nama_asli"]
        break

    # [2] fase/kelas
    print("")
    print("[2] Fase / Kelas   (E = Kelas X | F = Kelas XI atau XII)")
    while True:
        kelas = tanya("    Kelas (X / XI / XII)", "X").upper()
        if kelas in KELAS_FASE:
            break
        print("    \u274c Kelas harus X, XI, atau XII.")
    fase = KELAS_FASE[kelas]
    print("    \u2713 Fase %s / Kelas %s \u2014 semester: %s"
          % (fase, kelas, ", ".join("%d (%s)" % (a[0], a[2])
                                    for a in semester_aktif(fase, kelas))))
    if fase == "F" and kelas == "XII":
        print("    Catatan: Semester 5 = PKL, tidak ada pembelajaran di sekolah.")

    # [3][4] jp
    print("")
    jp = tanya_angka("[3] JP per minggu (struktur kurikulum)", 2)
    while True:
        jpp = tanya_angka("[4] JP dalam 1x pertemuan", jp)
        if jp % jpp == 0:
            break
        print("    \u274c %d tidak habis dibagi %d. Pilihan yang pas: %s"
              % (jp, jpp, ", ".join(str(d) for d in range(1, jp + 1) if jp % d == 0)))
    print("    \u2713 %d JP/minggu = %d pertemuan x %d JP" % (jp, jp // jpp, jpp))

    # [5] minggu efektif
    print("")
    print("[5] Jumlah minggu efektif (lihat kalender pendidikan sekolah)")
    mg = tanya_angka("    Semester GANJIL (lazim 17\u201322)", 22)
    mn = tanya_angka("    Semester GENAP  (lazim 13\u201316)", 15)

    # [6] lingkup
    print("")
    print("[6] Lingkup pembuatan RPM")
    print("      1. 2 semester penuh  \u2014 semua elemen Kelas %s (1 DOCX per elemen)" % kelas)
    print("      2. 1 semester        \u2014 (1 DOCX per elemen)")
    print("      3. 1 Elemen/CP       \u2014 1 DOCX saja")
    print("      4. Kelompok Tujuan Pembelajaran \u2014 pilih sendiri TP mana saja")
    print("         (mis. TP 1,2,4) menjadi 1 DOCX")
    print("      5. 1 Materi Pokok = 1 DOCX  \u2190 disarankan")
    print("         Satu CP dipecah menjadi beberapa Materi Pokok. Tiap Materi")
    print("         Pokok menjadi satu berkas yang memuat beberapa pertemuan")
    print("         sesuai alokasi waktu, jadi satu CP bisa menghasilkan 2-3")
    print("         berkas DOCX atau lebih.")
    print("    Catatan: pada semua pilihan, PENGALAMAN BELAJAR diuraikan untuk")
    print("    SETIAP pertemuan, sehingga jumlah halaman menyesuaikan alokasi waktu.")
    print("    Satu pertemuan dapat memuat lebih dari satu TP bila jumlah TP")
    print("    melebihi jumlah pertemuan yang tersedia.")
    while True:
        pilihan = tanya_angka("    Pilih nomor", 5)
        if 1 <= pilihan <= 5:
            break
        print("    \u274c Pilih 1, 2, 3, 4, atau 5.")
    semester = None
    elemen_no = None
    tp_pilih = None
    tp_per_tm = 1
    topik_n = None
    opsi = semester_aktif(fase, kelas)

    if pilihan == 1:
        lingkup = "tahun"
    elif pilihan == 2:
        lingkup = "semester"
        print("    Semester yang tersedia:")
        for a in opsi:
            print("      \u2022 %d \u2014 %s" % (a[0], a[1]))
        semester = tanya("    Isikan nomor semester atau tulis ganjil/genap",
                         str(opsi[0][0]))
    elif pilihan == 3:
        lingkup = "elemen"
        pra = susun_rencana(mapel, jp, kelas=kelas, fase=fase, mg=mg, mn=mn, jpp=jpp)
        daftar = detail_aktif(pra)
        print("    Elemen yang tersedia untuk Kelas %s:" % kelas)
        for d in daftar:
            print("      %d. %s  \u2014 %s | %d JP | %d pertemuan | %d TP"
                  % (d["urut_kelas"], topik_utama(d["elemen"]), d["label_semester"],
                     d["jp"], d["tm"], len(d.get("tp_list") or [])))
        elemen_no = tanya_angka("    Pilih nomor elemen", 1)
    elif pilihan == 4:
        lingkup = "tp"
        print("    Semester yang tersedia:")
        for a in opsi:
            print("      \u2022 %d \u2014 %s" % (a[0], a[1]))
        semester = tanya("    Isikan nomor semester atau tulis ganjil/genap",
                         str(opsi[0][0]))
        pra = susun_rencana(mapel, jp, kelas=kelas, fase=fase, mg=mg, mn=mn, jpp=jpp)
        sumber = detail_aktif(pra, semester)
        if not sumber:
            print("    \u274c Semester tersebut tidak tersedia untuk Kelas %s." % kelas)
            sys.exit(1)
        # ---- pilih Elemen/CP dahulu, baru TP milik CP itu ditampilkan ----
        print("")
        print("    Elemen/CP pada %s:" % sumber[0]["label_semester"])
        for d in sumber:
            print("      %d. %s" % (d["urut_kelas"], topik_utama(d["elemen"])))
            print("         %d JP | %d pertemuan | %d Tujuan Pembelajaran"
                  % (d["jp"], d["tm"], len(daftar_tp([d]))))
        print("      0. Semua Elemen/CP pada semester ini")
        while True:
            jwb_el = tanya("    Pilih nomor Elemen/CP", str(sumber[0]["urut_kelas"]))
            js = (jwb_el or "").strip().lower()
            if js in ("0", "semua", "all", "*"):
                elemen_no = None
                break
            if js.isdigit() and any(d["urut_kelas"] == int(js) for d in sumber):
                elemen_no = int(js)
                sumber = [d for d in sumber if d["urut_kelas"] == elemen_no]
                break
            print("    \u274c Nomor Elemen/CP tidak ada pada daftar di atas.")
        entri = daftar_tp(sumber)
        print("")
        print("    " + "-" * 68)
        if elemen_no:
            print("    ELEMEN %d: %s" % (elemen_no, topik_utama(sumber[0]["elemen"])))
            print("    Capaian Pembelajaran:")
            _cp = re.sub(r"\s+", " ", sumber[0]["cp"] or "").strip()
            _br, _ln = [], ""
            for _w in _cp.split(" "):
                if len(_ln) + len(_w) + 1 > 86:
                    _br.append(_ln)
                    _ln = _w
                else:
                    _ln = (_ln + " " + _w).strip()
            if _ln:
                _br.append(_ln)
            for _b in _br[:6]:
                print("      %s" % _b)
            print("    " + "-" * 68)
            print("    Tujuan Pembelajaran turunan dari Capaian Pembelajaran di atas:")
        else:
            print("    DAFTAR TUJUAN PEMBELAJARAN \u2014 %s" % sumber[0]["label_semester"])
            print("    " + "-" * 68)
        elemen_kini = None
        for e in entri:
            if not elemen_no and e["elemen"] != elemen_kini:
                elemen_kini = e["elemen"]
                print("    [Elemen] %s" % topik_utama(elemen_kini))
            teks = re.sub(r"^\s*\d+\.\s*", "", e["tp"])
            print("      %2d. %s" % (e["no"], teks[:96]))
        print("    " + "-" * 68)
        print("    Tersedia %d TP. Satu pertemuan = %d JP = %d menit."
              % (len(entri), jpp, jpp * 45))
        print("")
        while True:
            jwb = tanya("    TP mana yang dijadikan RPM ini? (mis. 1,2,4 / 1-5 / semua)",
                        "semua")
            nomor, galat = urai_pilihan(jwb, len(entri))
            if nomor:
                break
            print("    \u274c %s" % galat)
        tp_pilih = ",".join(str(x) for x in nomor)
        print("    \u2713 %d TP dipilih: %s" % (len(nomor), tp_pilih))
        print("")
        print("    Berapa Tujuan Pembelajaran yang dirangkum dalam 1x pertemuan?")
        print("    (1 = satu TP satu pertemuan; 2 = dua TP dirangkum jadi satu pertemuan)")
        tp_per_tm = tanya_angka("    TP per pertemuan", 1)
        n_tm = int(math.ceil(float(len(nomor)) / tp_per_tm))
        print("    \u2713 %d TP \u00f7 %d TP/pertemuan = %d pertemuan = %d JP (%d menit)"
              % (len(nomor), tp_per_tm, n_tm, n_tm * jpp, n_tm * jpp * 45))
    else:
        lingkup = "topik"
        print("    Semester yang tersedia:")
        for a in opsi:
            print("      \u2022 %d \u2014 %s" % (a[0], a[1]))
        semester = tanya("    Isikan nomor semester atau tulis ganjil/genap",
                         str(opsi[0][0]))
        pra = susun_rencana(mapel, jp, kelas=kelas, fase=fase, mg=mg, mn=mn, jpp=jpp)
        sumber = detail_aktif(pra, semester)
        if not sumber:
            print("    \u274c Semester tersebut tidak tersedia untuk Kelas %s." % kelas)
            sys.exit(1)
        print("")
        print("    Elemen/CP pada %s:" % sumber[0]["label_semester"])
        for d in sumber:
            print("      %d. %s" % (d["urut_kelas"], topik_utama(d["elemen"])))
            print("         %d JP | %d pertemuan | %d Tujuan Pembelajaran"
                  % (d["jp"], d["tm"], len(daftar_tp([d]))))
        print("      0. Semua Elemen/CP pada semester ini")
        while True:
            jwb_el = tanya("    Pilih nomor Elemen/CP", str(sumber[0]["urut_kelas"]))
            js = (jwb_el or "").strip().lower()
            if js in ("0", "semua", "all", "*"):
                elemen_no = None
                break
            if js.isdigit() and any(d["urut_kelas"] == int(js) for d in sumber):
                elemen_no = int(js)
                sumber = [d for d in sumber if d["urut_kelas"] == elemen_no]
                break
            print("    \u274c Nomor Elemen/CP tidak ada pada daftar di atas.")
        print("")
        print("    Satu berkas RPM = satu Materi Pokok = beberapa pertemuan.")
        print("    Kosongkan untuk pembagian otomatis (\u00b1 3 pertemuan per Materi Pokok).")
        while True:
            jwb_t = tanya("    Jumlah Materi Pokok per Elemen/CP", "otomatis")
            js = (jwb_t or "").strip().lower()
            if js in ("", "otomatis", "auto", "0"):
                topik_n = None
                break
            if js.isdigit() and int(js) > 0:
                topik_n = int(js)
                break
            print("    \u274c Isikan angka, atau kosongkan untuk otomatis.")
        print("")
        print("    " + "-" * 68)
        print("    RENCANA PEMBAGIAN BERKAS")
        print("    " + "-" * 68)
        jml = 0
        for d in sumber:
            butir_cp = daftar_tp([d])
            grup = bagi_topik(butir_cp, d["tm"], topik_n, det=d)
            print("    [Elemen %d] %s" % (d["urut_kelas"], topik_utama(d["elemen"])))
            print("        %d JP | %d pertemuan | %d TP \u2192 %d berkas DOCX"
                  % (d["jp"], d["tm"], len(butir_cp), len(grup)))
            for i, g in enumerate(grup, 1):
                print("        %d) %s" % (i, g["nama"]))
                per_tm = float(len(g["butir"])) / g["tm"] if g["tm"] else 0
                print("           %d TP | %d pertemuan | %d JP | %.1f TP per pertemuan"
                      % (len(g["butir"]), g["tm"], g["tm"] * jpp, per_tm))
            jml += len(grup)
        print("    " + "-" * 68)
        print("    Jumlah berkas DOCX yang akan dibuat: %d" % jml)

    # [7] identitas
    print("")
    print("[7] Identitas dokumen (dikosongkan = dibuat titik-titik untuk diisi tangan)")
    instansi = tanya("    Nama Instansi", "SMKS PGRI 2 Jambi")
    npsn = tanya("    NPSN", "...........................")
    print("    Kepala Sekolah sudah tetap: %s (%s)" % (KEPALA_NAMA, KEPALA_NPA))
    penyusun = tanya("    Penyusun (nama Guru Mata Pelajaran)", "...........................")
    npa = tanya("    NPA Guru Mata Pelajaran", "")
    rombel = tanya("    Kelas/rombel yang ditulis di RPM (mis. X TSM 1)", kelas)
    kota = tanya("    Kota/kabupaten (untuk kolom tanda tangan)", "Jambi")
    tahun = tanya("    Tahun Pelajaran", "2026/2027")

    # [8] daftar murid
    print("")
    print("[8] Daftar murid untuk form penilaian")
    print("    Kosongkan untuk memakai 10 nama contoh, atau isikan nama berkas .txt")
    print("    (satu nama per baris) yang berada di folder yang sama.")
    mf = tanya("    Berkas daftar murid", "")
    if mf and not os.path.isabs(mf):
        kandidat = [os.path.join(BASE_OUT, mf),
                    os.path.join(os.path.dirname(os.path.abspath(__file__)), mf),
                    mf]
        for k in kandidat:
            if os.path.isfile(k):
                mf = k
                break
    print("")
    print("-" * 74)
    print("Memproses...")
    print("")

    generate_rpm(mapel, jp, kelas=kelas, fase=fase, mg=mg, mn=mn, jpp=jpp,
                 lingkup=lingkup, semester=semester, elemen=elemen_no,
                 tp=tp_pilih, tp_per_tm=tp_per_tm, topik_n=topik_n,
                 instansi=instansi, npsn=npsn, penyusun=penyusun, npa=npa,
                 rombel=rombel, kota=kota, tahun=tahun, murid_file=(mf or None))


# ============================================================================
# 12. CLI
# ============================================================================


def pilih_mapel_form(db, urut="[1]"):
    """Tanya nama mata pelajaran lalu kembalikan sumber CP yang dipilih."""
    while True:
        q = tanya("%s Mata Pelajaran" % urut, wajib=True)
        kand = cari_mapel(db, q)
        if not kand:
            print("    \u274c Tidak ditemukan. Coba kata kunci lain.")
            continue
        if len(kand) == 1:
            pilih = kand[0]
        else:
            print("    Ditemukan beberapa pilihan:")
            for i, s in enumerate(kand[:10]):
                print("      %d. %s  [Lampiran %s %s, fase: %s]"
                      % (i + 1, s["nama_asli"].title(), s["lampiran"], s["kode"],
                         ", ".join(sorted(s["fase"]))))
            n = tanya_angka("    Pilih nomor", 1)
            if n > len(kand[:10]):
                continue
            pilih = kand[n - 1]
        print("    \u2713 %s  [Lampiran %s %s, fase tersedia: %s]"
              % (pilih["nama_asli"].title(), pilih["lampiran"], pilih["kode"],
                 ", ".join(sorted(pilih["fase"]))))
        return pilih


def mode_form_ringkas():
    """Form pokok RPM.

    Yang ditanyakan hanya: mata pelajaran, kelas, JP dalam 1 minggu, jumlah
    pertemuan dalam 1 minggu, minggu efektif tiap semester, bentuk berkas, dan
    identitas. Pembagian Tujuan Pembelajaran ke dalam pertemuan, penentuan
    Materi Pokok, dan jumlah berkas RPM dihitung sendiri dari alokasi waktu.
    """
    db = load_db()
    print("=" * 74)
    print("  GENERATOR RPM \u2014 RANCANGAN PEMBELAJARAN MENDALAM")
    print("  Kurikulum Nasional 2025 \u2014 SMK/MAK")
    print("=" * 74)
    print("  Elemen, Capaian Pembelajaran, Tujuan Pembelajaran, dan alokasi waktu")
    print("  diambil dari mesin ATP, sehingga ATP, RPM, dan KKTP selalu sinkron.")
    print("-" * 74)

    s = pilih_mapel_form(db, "[1]")
    mapel = s["nama_asli"]

    print("")
    while True:
        kelas = tanya("[2] Kelas (X / XI / XII)", "X").upper()
        if kelas in KELAS_FASE:
            break
        print("    \u274c Kelas harus X, XI, atau XII.")
    fase = KELAS_FASE[kelas]
    aktif = semester_aktif(fase, kelas)
    print("    \u2713 Fase %s / Kelas %s \u2014 semester: %s"
          % (fase, kelas, ", ".join("%d (%s)" % (a[0], a[2]) for a in aktif)))
    if fase == "F" and kelas == "XII":
        print("    Catatan: Semester 5 = PKL, tidak dibuatkan RPM.")

    print("")
    jp = tanya_angka("[3] JP dalam 1 minggu (struktur kurikulum)", 2)
    while True:
        tm = tanya_angka("[4] Jumlah pertemuan dalam 1 minggu", 1)
        if tm >= 1 and jp % tm == 0:
            break
        pas = ", ".join(str(d) for d in range(1, jp + 1) if jp % d == 0)
        print("    \u274c %d JP tidak habis dibagi %d pertemuan. Pilihan yang pas: %s"
              % (jp, tm, pas))
    jpp = jp // tm
    print("    \u2713 %d JP/minggu = %d pertemuan x %d JP (%d menit per pertemuan)"
          % (jp, tm, jpp, jpp * 45))

    print("")
    print("[5] Jumlah minggu efektif dalam 1 semester (minggu, bukan pertemuan)")
    mg = tanya_angka("      - Semester Ganjil", 22)
    mn = tanya_angka("      - Semester Genap", 15)

    print("")
    print("[6] Bentuk berkas RPM")
    print("    1. Otomatis per Materi Pokok  \u2014 1 Materi Pokok = 1 berkas DOCX;")
    print("       satu CP dipecah menjadi beberapa Materi Pokok sesuai alokasi waktu.")
    print("    2. 1 Capaian Pembelajaran = 1 berkas DOCX \u2014 seluruh Tujuan")
    print("       Pembelajaran dan seluruh pertemuannya digabung (hemat kertas).")
    bentuk = tanya_angka("    Pilihan [1/2]", 1)
    bentuk = 2 if bentuk == 2 else 1

    # ---------------- pratinjau rencana ----------------
    r = susun_rencana(mapel, jp, kelas=kelas, fase=fase, mg=mg, mn=mn, jpp=jpp)
    print("")
    print("-" * 74)
    print(" RENCANA PEMBAGIAN BERKAS")
    print("-" * 74)
    total = 0
    for satu in aktif:
        no, label = satu[0], satu[1]
        sumber = detail_aktif(r, str(no))
        if not sumber:
            continue
        minggu = mg if "GANJIL" in label.upper() else mn
        print(" %s \u2014 %d minggu efektif x %d JP = %d JP | %d pertemuan"
              % (label, minggu, jp, minggu * jp, minggu * tm))
        for d in sumber:
            butir = daftar_tp([d])
            if bentuk == 1:
                grup = bagi_topik(butir, d["tm"], None, det=d)
                print("   CP %d. %-40s %d TP | %d JP | %d pertemuan \u2192 %d berkas"
                      % (d["urut_kelas"], topik_utama(d["elemen"])[:40],
                         len(butir), d["jp"], d["tm"], len(grup)))
                for g in grup:
                    print("        \u2022 %-46s %d TP | %d pertemuan"
                          % (g["nama"][:46], len(g["butir"]), g["tm"]))
                total += len(grup)
            else:
                print("   CP %d. %-40s %d TP | %d JP | %d pertemuan \u2192 1 berkas"
                      % (d["urut_kelas"], topik_utama(d["elemen"])[:40],
                         len(butir), d["jp"], d["tm"]))
                total += 1
    print("-" * 74)
    print(" TOTAL BERKAS RPM YANG AKAN DIBUAT: %d" % total)
    print("-" * 74)

    # ---------------- identitas ----------------
    print("")
    print("[7] Identitas dokumen")
    instansi = tanya("    Satuan Pendidikan", "SMKS PGRI 2 Jambi")
    npsn = tanya("    NPSN", "...........................")
    rombel = tanya("    Kelas/rombel yang ditulis di RPM (mis. X TSM 1)", kelas)
    kota = tanya("    Kota/kabupaten (untuk kolom tanda tangan)", "Jambi")
    tahun = tanya("    Tahun Pelajaran", "2026/2027")

    print("")
    print("[8] Guru Mata Pelajaran (wajib \u2014 untuk identitas dan tanda tangan)")
    print("    Kepala Sekolah sudah tetap: %s (%s)" % (KEPALA_NAMA, KEPALA_NPA))
    while True:
        penyusun = tanya("    Nama/Penyusun", "")
        if penyusun.strip(". "):
            break
        print("    \u274c Nama Guru Mata Pelajaran harus diisi.")
    while True:
        npa = tanya("    NPA", "")
        if npa.strip(". "):
            break
        print("    \u274c NPA Guru Mata Pelajaran harus diisi.")

    print("")
    print("[9] Daftar murid untuk form penilaian")
    print("    Kosongkan untuk memakai 10 nama contoh, atau isikan nama berkas .txt")
    mf = tanya("    Berkas daftar murid", "")
    if mf and not os.path.isabs(mf):
        for k in [os.path.join(BASE_OUT, mf),
                  os.path.join(os.path.dirname(os.path.abspath(__file__)), mf), mf]:
            if os.path.isfile(k):
                mf = k
                break

    print("")
    print("-" * 74)
    print("Memproses %d berkas..." % total)
    print("")

    umum = dict(kelas=kelas, fase=fase, mg=mg, mn=mn, jpp=jpp,
                instansi=instansi, npsn=npsn, penyusun=penyusun, npa=npa,
                rombel=rombel, kota=kota, tahun=tahun, murid_file=(mf or None))
    if bentuk == 1:
        for satu in aktif:
            no = satu[0]
            if not detail_aktif(r, str(no)):
                continue
            generate_rpm(mapel, jp, lingkup="topik", semester=str(no), **umum)
    else:
        generate_rpm(mapel, jp, lingkup="tahun", **umum)


def main():
    ap = argparse.ArgumentParser(
        description="Generator RPM (Rancangan Pembelajaran Mendalam) \u2014 "
                    "Kurikulum Nasional 2025, sinkron dengan generator ATP.")
    ap.add_argument("--form", action="store_true",
                    help="mode tanya-jawab ringkas (disarankan)")
    ap.add_argument("--lanjutan", action="store_true",
                    help="mode tanya-jawab dengan pengaturan lingkup/TP secara rinci")
    ap.add_argument("--mapel")
    ap.add_argument("--jp", type=int, help="JP per minggu")
    ap.add_argument("--jpp", "--jp-pertemuan", dest="jpp", type=int,
                    help="JP dalam 1x pertemuan")
    ap.add_argument("--kelas", help="X / XI / XII")
    ap.add_argument("--fase", help="E / F")
    ap.add_argument("--mg", "--minggu-ganjil", dest="mg", type=int, default=22)
    ap.add_argument("--mn", "--minggu-genap", dest="mn", type=int, default=15)
    ap.add_argument("--menit", type=int, default=45)
    ap.add_argument("--lingkup", default="tahun",
                    help="tahun | semester | elemen | tp | topik")
    ap.add_argument("--topik-per-cp", dest="topik_n", type=int, default=None,
                    help="jumlah Materi Pokok per CP untuk --lingkup topik "
                         "(kosong = otomatis)")
    ap.add_argument("--tp", help="nomor TP untuk --lingkup tp, mis. \"1,2,4\" atau \"1-5\"")
    ap.add_argument("--tp-per-pertemuan", dest="tp_per_tm", type=int, default=1,
                    help="jumlah TP yang dirangkum dalam 1x pertemuan")
    ap.add_argument("--semester", help="nomor semester atau ganjil/genap")
    ap.add_argument("--elemen", type=int, help="nomor elemen pada kelas tersebut")
    ap.add_argument("--instansi", default="SMKS PGRI 2 Jambi")
    ap.add_argument("--npsn", default="...........................")
    ap.add_argument("--penyusun", default="...........................")
    ap.add_argument("--npa", default=None, help="NPA Guru Mata Pelajaran")
    ap.add_argument("--rombel")
    ap.add_argument("--kota", default="Jambi")
    ap.add_argument("--tahun", default="2026/2027")
    ap.add_argument("--murid", help="berkas .txt daftar nama murid (1 nama per baris)")
    ap.add_argument("--elemen-kelas11", dest="elemen_kelas", type=int,
                    help="jumlah elemen untuk Kelas XI (Fase F)")
    ap.add_argument("--out-dir")
    ap.add_argument("--list", dest="cari", help="cari nama mata pelajaran")
    a = ap.parse_args()

    if a.cari:
        db = load_db()
        hasil = cari_mapel(db, a.cari)
        if not hasil:
            print("Tidak ada yang cocok.")
            return
        for s in hasil[:25]:
            print("- %-58s [Lampiran %s %s, fase: %s]"
                  % (s["nama_asli"].title(), s["lampiran"], s["kode"],
                     ", ".join(sorted(s["fase"]))))
        return

    if a.lanjutan:
        mode_form()
        return

    if a.form or not a.mapel:
        mode_form_ringkas()
        return

    if not a.jp:
        print("\u274c --jp (JP per minggu) wajib diisi. Atau pakai --form.")
        sys.exit(1)

    generate_rpm(a.mapel, a.jp, kelas=a.kelas, fase=a.fase, mg=a.mg, mn=a.mn,
                 menit=a.menit, jpp=a.jpp, lingkup=a.lingkup, semester=a.semester,
                 elemen=a.elemen, tp=a.tp, tp_per_tm=a.tp_per_tm,
                 topik_n=a.topik_n,
                 instansi=a.instansi, npsn=a.npsn,
                 penyusun=a.penyusun, npa=a.npa, rombel=a.rombel, kota=a.kota,
                 tahun=a.tahun,
                 murid_file=a.murid, elemen_kelas=a.elemen_kelas, out_dir=a.out_dir)


if __name__ == "__main__":
    main()
